/* * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. * Copyright (C) 2009 Volker Berlin (i-net software) * Copyright (C) 2010 Karsten Heinrich (i-net software) * 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.awt.shell; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; import java.util.concurrent.*; import javax.swing.SwingConstants; import cli.System.IntPtr; import cli.System.Drawing.Bitmap; // NOTE: This class basically a conversion of the OpenJDK Wen32ShellFolder2, but uses // .NET pointers and objects instead of representing pointers as long /** * Win32 Shell Folders *
*
* There are two fundamental types of shell folders : file system folders
* and non-file system folders. File system folders are relatively easy
* to deal with. Non-file system folders are items such as My Computer,
* Network Neighborhood, and the desktop. Some of these non-file system
* folders have special values and properties.
*
*
* Win32 keeps two basic data structures for shell folders. The first
* of these is called an ITEMIDLIST. Usually a pointer, called an
* LPITEMIDLIST, or more frequently just "PIDL". This structure holds
* a series of identifiers and can be either relative to the desktop
* (an absolute PIDL), or relative to the shell folder that contains them.
* Some Win32 functions can take absolute or relative PIDL values, and
* others can only accept relative values.
*
* The second data structure is an IShellFolder COM interface. Using
* this interface, one can enumerate the relative PIDLs in a shell
* folder, get attributes, etc.
*
* All Win32ShellFolder2 objects which are folder types (even non-file
* system folders) contain an IShellFolder object. Files are named in
* directories via relative PIDLs.
*
* @author Michael Martak
* @author Leif Samuelsson
* @author Kenneth Russell
* @author Volker Berlin
* @author Karsten Heinrich
* @since 1.4 */
final class Win32ShellFolder2 extends ShellFolder {
// Win32 Shell Folder Constants
public static final int DESKTOP = 0x0000;
public static final int INTERNET = 0x0001;
public static final int PROGRAMS = 0x0002;
public static final int CONTROLS = 0x0003;
public static final int PRINTERS = 0x0004;
public static final int PERSONAL = 0x0005;
public static final int FAVORITES = 0x0006;
public static final int STARTUP = 0x0007;
public static final int RECENT = 0x0008;
public static final int SENDTO = 0x0009;
public static final int BITBUCKET = 0x000a;
public static final int STARTMENU = 0x000b;
public static final int DESKTOPDIRECTORY = 0x0010;
public static final int DRIVES = 0x0011;
public static final int NETWORK = 0x0012;
public static final int NETHOOD = 0x0013;
public static final int FONTS = 0x0014;
public static final int TEMPLATES = 0x0015;
public static final int COMMON_STARTMENU = 0x0016;
public static final int COMMON_PROGRAMS = 0X0017;
public static final int COMMON_STARTUP = 0x0018;
public static final int COMMON_DESKTOPDIRECTORY = 0x0019;
public static final int APPDATA = 0x001a;
public static final int PRINTHOOD = 0x001b;
public static final int ALTSTARTUP = 0x001d;
public static final int COMMON_ALTSTARTUP = 0x001e;
public static final int COMMON_FAVORITES = 0x001f;
public static final int INTERNET_CACHE = 0x0020;
public static final int COOKIES = 0x0021;
public static final int HISTORY = 0x0022;
// Win32 shell folder attributes
public static final int ATTRIB_CANCOPY = 0x00000001;
public static final int ATTRIB_CANMOVE = 0x00000002;
public static final int ATTRIB_CANLINK = 0x00000004;
public static final int ATTRIB_CANRENAME = 0x00000010;
public static final int ATTRIB_CANDELETE = 0x00000020;
public static final int ATTRIB_HASPROPSHEET = 0x00000040;
public static final int ATTRIB_DROPTARGET = 0x00000100;
public static final int ATTRIB_LINK = 0x00010000;
public static final int ATTRIB_SHARE = 0x00020000;
public static final int ATTRIB_READONLY = 0x00040000;
public static final int ATTRIB_GHOSTED = 0x00080000;
public static final int ATTRIB_HIDDEN = 0x00080000;
public static final int ATTRIB_FILESYSANCESTOR = 0x10000000;
public static final int ATTRIB_FOLDER = 0x20000000;
public static final int ATTRIB_FILESYSTEM = 0x40000000;
public static final int ATTRIB_HASSUBFOLDER = 0x80000000;
public static final int ATTRIB_VALIDATE = 0x01000000;
public static final int ATTRIB_REMOVABLE = 0x02000000;
public static final int ATTRIB_COMPRESSED = 0x04000000;
public static final int ATTRIB_BROWSABLE = 0x08000000;
public static final int ATTRIB_NONENUMERATED = 0x00100000;
public static final int ATTRIB_NEWCONTENT = 0x00200000;
// IShellFolder::GetDisplayNameOf constants
public static final int SHGDN_NORMAL = 0;
public static final int SHGDN_INFOLDER = 1;
public static final int SHGDN_INCLUDE_NONFILESYS= 0x2000;
public static final int SHGDN_FORADDRESSBAR = 0x4000;
public static final int SHGDN_FORPARSING = 0x8000;
// Values for system call LoadIcon()
public enum SystemIcon {
IDI_APPLICATION(32512),
IDI_HAND(32513),
IDI_ERROR(32513),
IDI_QUESTION(32514),
IDI_EXCLAMATION(32515),
IDI_WARNING(32515),
IDI_ASTERISK(32516),
IDI_INFORMATION(32516),
IDI_WINLOGO(32517);
private final int iconID;
SystemIcon(int iconID) {
this.iconID = iconID;
}
public int getIconID() {
return iconID;
}
}
static class FolderDisposer implements sun.java2d.DisposerRecord {
/*
* This is cached as a concession to getFolderType(), which needs
* an absolute PIDL.
*/
cli.System.IntPtr absolutePIDL;
/*
* We keep track of shell folders through the IShellFolder
* interface of their parents plus their relative PIDL.
*/
cli.System.Object pIShellFolder;
cli.System.IntPtr relativePIDL;
boolean disposed;
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
public void dispose() {
if (disposed)
return;
if ( relativePIDL != null && !cli.System.IntPtr.Zero.Equals( relativePIDL ) ) {
releasePIDL(relativePIDL);
}
if ( absolutePIDL != null && !cli.System.IntPtr.Zero.Equals( absolutePIDL ) ) {
releasePIDL(absolutePIDL);
}
if ( pIShellFolder != null ) {
releaseIShellFolder(pIShellFolder);
}
disposed = true;
}
}
FolderDisposer disposer = new FolderDisposer();
private void setIShellFolder( cli.System.Object iShellFolder ) {
disposer.pIShellFolder = iShellFolder;
}
private void setRelativePIDL(cli.System.IntPtr relativePIDL) {
disposer.relativePIDL = relativePIDL;
}
/*
* The following are for caching various shell folder properties.
*/
private cli.System.Object pIShellIcon = null;
private String folderType = null;
private String displayName = null;
private Image smallIcon = null;
private Image largeIcon = null;
private Boolean isDir = null;
/*
* The following is to identify the My Documents folder as being special
*/
private boolean isPersonal;
/**
* Create a system special shell folder, such as the
* desktop or Network Neighborhood.
*/
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {
// Desktop is parent of DRIVES and NETWORK, not necessarily
// other special shell folders.
super ( null, (getFileSystemPath(csidl) == null) ? ("ShellFolder: 0x" + Integer.toHexString(csidl)) : getFileSystemPath(csidl));
if (csidl == DESKTOP) {
// compared to the Java implementation we require two steps here since
// we don't have a callback from the native methods in to this instance
setIShellFolder( initDesktopFolder() );
setRelativePIDL( initDesktopPIDL() );
} else {
cli.System.Object desktopFolder = getDesktop().getIShellFolder();
cli.System.IntPtr pidl = initSpecialPIDL( desktopFolder, csidl );
setRelativePIDL( pidl );
setIShellFolder( initSpecialFolder(desktopFolder, pidl) );
// At this point, the native method initSpecial() has set our relativePIDL
// relative to the Desktop, which may not be our immediate parent. We need
// to traverse this ID list and break it into a chain of shell folders from
// the top, with each one having an immediate parent and a relativePIDL
// relative to that parent.
bindToDesktop();
}
sun.java2d.Disposer.addRecord(this , disposer);
}
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
protected void bindToDesktop() {
cli.System.IntPtr pIDL = disposer.relativePIDL;
parent = getDesktop();
while ( pIDL != null && !cli.System.IntPtr.Zero.Equals( pIDL ) ) {
// Get a child pidl relative to 'parent'
cli.System.IntPtr childPIDL = copyFirstPIDLEntry(pIDL);
if (childPIDL != null && !cli.System.IntPtr.Zero.Equals( childPIDL ) ) {
// Get a handle to the the rest of the ID list
// i,e, parent's grandchilren and down
pIDL = getNextPIDLEntry(pIDL);
if ( pIDL != null && !cli.System.IntPtr.Zero.Equals( pIDL ) ) {
// Now we know that parent isn't immediate to 'this' because it
// has a continued ID list. Create a shell folder for this child
// pidl and make it the new 'parent'.
parent = new Win32ShellFolder2( (Win32ShellFolder2) parent, childPIDL );
} else {
// No grandchildren means we have arrived at the parent of 'this',
// and childPIDL is directly relative to parent.
disposer.relativePIDL = childPIDL;
}
} else {
break;
}
}
}
/**
* Create a system shell folder
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
Win32ShellFolder2(Win32ShellFolder2 parent, cli.System.Object pIShellFolder, cli.System.IntPtr relativePIDL, String path) {
super(parent, (path != null) ? path : "ShellFolder: ");
this.disposer.pIShellFolder = pIShellFolder;
this.disposer.relativePIDL = relativePIDL;
sun.java2d.Disposer.addRecord(this, disposer);
}
/**
* Creates a shell folder with a parent and relative PIDL
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
Win32ShellFolder2(Win32ShellFolder2 parent, cli.System.IntPtr relativePIDL) {
super (parent, getFileSystemPath(parent.getIShellFolder(), relativePIDL));
this .disposer.relativePIDL = relativePIDL;
getAbsolutePath();
sun.java2d.Disposer.addRecord(this , disposer);
}
// Initializes the desktop shell folder
/**
* Returns the pIDL of the Desktop folder (pIDL root)
* @return the pIDL of the Desktop folder (pIDL root)
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
private static native cli.System.IntPtr initDesktopPIDL();
/**
* Returns the IShellFolder pointer of the Desktop folder (pIDL root)
* @return the IShellFolder pointer of the Desktop folder (pIDL root)
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
private static native cli.System.Object initDesktopFolder();
// Initializes a special, non-file system shell folder
// from one of the above constants
/**
* initializes a special folder
* @param desktopIShellFolder the IShellFolder reference of the desktop folder
* @param csidl the CSIDL of the requested special folder
* @return the pIDL of the special folder relative to the desktop root
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
private static native cli.System.IntPtr initSpecialPIDL(cli.System.Object desktopIShellFolder, int csidl);
/**
* initializes a special folder
* @param desktopIShellFolder the IShellFolder reference of the desktop folder
* @param pidl the pIDL of the requested folder relative to the desktopIShellFolder
* @return the IShellFolder reference for the requested folder
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
private static native cli.System.Object initSpecialFolder(cli.System.Object desktopIShellFolder, cli.System.IntPtr pidl);
/** Marks this folder as being the My Documents (Personal) folder */
public void setIsPersonal() {
isPersonal = true;
}
/**
* This method is implemented to make sure that no instances
* of ShellFolder
are ever serialized. If isFileSystem()
returns
* true
, then the object is representable with an instance of
* java.io.File
instead. If not, then the object depends
* on native PIDL state and should not be serialized.
*
* @returns a java.io.File
replacement object. If the folder
* is a not a normal directory, then returns the first non-removable
* drive (normally "C:\").
*/
protected Object writeReplace()
throws java.io.ObjectStreamException {
if (isFileSystem()) {
return new File(getPath());
} else {
Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
if (drives != null) {
File[] driveRoots = drives.listFiles();
if (driveRoots != null) {
for (int i = 0; i < driveRoots.length; i++) {
if (driveRoots[i] instanceof Win32ShellFolder2) {
Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];
if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
return new File(sf.getPath());
}
}
}
}
}
// Ouch, we have no hard drives. Return something "valid" anyway.
return new File("C:\\");
}
}
/**
* Finalizer to clean up any COM objects or PIDLs used by this object.
*/
protected void dispose() {
disposer.dispose();
}
// Given a (possibly multi-level) relative PIDL (with respect to
// the desktop, at least in all of the usage cases in this code),
// return a pointer to the next entry. Does not mutate the PIDL in
// any way. Returns 0 if the null terminator is reached.
// Needs to be accessible to Win32ShellFolderManager2
@cli.System.Security.SecurityCriticalAttribute.Annotation
static native cli.System.IntPtr getNextPIDLEntry(cli.System.IntPtr pIDL);
// Given a (possibly multi-level) relative PIDL (with respect to
// the desktop, at least in all of the usage cases in this code),
// copy the first entry into a newly-allocated PIDL. Returns 0 if
// the PIDL is at the end of the list.
// Needs to be accessible to Win32ShellFolderManager2
@cli.System.Security.SecurityCriticalAttribute.Annotation
static native cli.System.IntPtr copyFirstPIDLEntry(cli.System.IntPtr pIDL);
// Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL
/**
* Combines a parent pIDL with a descendant pIDL. It doesn't matter whether the parent pIDL
* is relative or absolute since this is only a concatenation of the IDLs
* @param ppIDL the parent pIDL
* @param pIDL the pIDL relative to the ppIDL
* @return a pIDL for the item referenced by the original pIDL but relative to the parent of ppIDL
*/
@cli.System.Security.SecurityCriticalAttribute.Annotation
private static native cli.System.IntPtr combinePIDLs(cli.System.IntPtr ppIDL, cli.System.IntPtr pIDL);
// Release a PIDL object
// Needs to be accessible to Win32ShellFolderManager2
@cli.System.Security.SecurityCriticalAttribute.Annotation
static native void releasePIDL(cli.System.IntPtr pIDL);
// Release an IShellFolder object
@cli.System.Security.SecurityCriticalAttribute.Annotation
static native void releaseIShellFolder( cli.System.Object iShellFolder );
/**
* Accessor for IShellFolder
*/
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
public cli.System.Object getIShellFolder() {
if (disposer.pIShellFolder == null ) {
assert (isDirectory());
assert (parent != null);
cli.System.Object parentIShellFolder = getParentIShellFolder();
if (parentIShellFolder == null) {
throw new InternalError( "Parent IShellFolder was null for " + getAbsolutePath() );
}
// We are a directory with a parent and a relative PIDL.
// We want to bind to the parent so we get an IShellFolder instance associated with us.
disposer.pIShellFolder = bindToObject(parentIShellFolder, disposer.relativePIDL);
if (disposer.pIShellFolder == null ) {
throw new InternalError("Unable to bind " + getAbsolutePath() + " to parent");
}
}
return disposer.pIShellFolder;
}
/**
* Get the parent ShellFolder's IShellFolder interface
*/
public cli.System.Object getParentIShellFolder() {
Win32ShellFolder2 parent = (Win32ShellFolder2) getParentFile();
cli.System.Object parentFolder;
if (parent == null) {
// Parent should only be null if this is the desktop, whose
// relativePIDL is relative to its own IShellFolder.
parentFolder = getIShellFolder();
} else {
parentFolder = parent.getIShellFolder();
}
return parentFolder;
}
/**
* Accessor for relative PIDL
*/
public cli.System.IntPtr getRelativePIDL() {
if (disposer.relativePIDL == null) {
throw new InternalError( "Should always have a relative PIDL" );
}
return disposer.relativePIDL;
}
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
private cli.System.IntPtr getAbsolutePIDL() {
if (parent == null) {
// This is the desktop
return getRelativePIDL();
} else {
if (disposer.absolutePIDL == null || disposer.absolutePIDL.Equals( IntPtr.Zero )) {
disposer.absolutePIDL = combinePIDLs( ((Win32ShellFolder2) parent).getAbsolutePIDL(), getRelativePIDL());
}
return disposer.absolutePIDL;
}
}
/**
* Helper function to return the desktop
*/
public Win32ShellFolder2 getDesktop() {
return Win32ShellFolderManager2.getDesktop();
}
/**
* Helper function to return the desktop IShellFolder interface
*/
public cli.System.Object getDesktopIShellFolder() {
return getDesktop().getIShellFolder();
}
private static boolean pathsEqual(String path1, String path2) {
// Same effective implementation as Win32FileSystem
return path1.equalsIgnoreCase(path2);
}
/**
* Check to see if two ShellFolder objects are the same
*/
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
public boolean equals(Object o) {
if (o == null || !(o instanceof Win32ShellFolder2)) {
// Short-circuit circuitous delegation path
if (!(o instanceof File)) {
return super.equals(o);
}
return pathsEqual(getPath(), ((File) o).getPath());
}
Win32ShellFolder2 rhs = (Win32ShellFolder2) o;
if ((parent == null && rhs.parent != null) ||
(parent != null && rhs.parent == null)) {
return false;
}
if (isFileSystem() && rhs.isFileSystem()) {
// Only folders with identical parents can be equal
return (pathsEqual(getPath(), rhs.getPath()) &&
(parent == rhs.parent || parent.equals(rhs.parent)));
}
if (parent == rhs.parent || parent.equals(rhs.parent)) {
try {
return pidlsEqual(getParentIShellFolder(), disposer.relativePIDL, rhs.disposer.relativePIDL);
} catch (InterruptedException e) {
return false;
}
}
return false;
}
@cli.System.Security.SecurityCriticalAttribute.Annotation
static boolean pidlsEqual(final cli.System.Object pIShellFolder, final cli.System.IntPtr pidl1, final cli.System.IntPtr pidl2)
throws InterruptedException {
return invoke(new Callablenull
if this shellfolder does not denote a directory.
*/
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
public File[] listFiles(final boolean includeHiddenFiles) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(getPath());
}
try {
return invoke(new CallableImage
*/
static Image getShell32Icon(int iconID, boolean getLargeIcon) {
Bitmap bitmap = getShell32IconResourceAsBitmap(iconID, getLargeIcon);
if (bitmap == null) {
return null;
}
return new BufferedImage(bitmap);
}
private static native Bitmap getShell32IconResourceAsBitmap(int iconID, boolean getLargeIcon);
/**
* Returns the canonical form of this abstract pathname. Equivalent to
* new Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())
.
*
* @see java.io.File#getCanonicalFile
*/
public File getCanonicalFile() throws IOException {
return this;
}
/*
* Indicates whether this is a special folder (includes My Documents)
*/
public boolean isSpecial() {
return isPersonal || !isFileSystem() || (this == getDesktop());
}
/**
* Compares this object with the specified object for order.
*
* @see sun.awt.shell.ShellFolder#compareTo(File)
*/
public int compareTo(File file2) {
if (!(file2 instanceof Win32ShellFolder2)) {
if (isFileSystem() && !isSpecial()) {
return super.compareTo(file2);
} else {
return -1; // Non-file shellfolders sort before files
}
}
return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);
}
// native constants from commctrl.h
private static final int LVCFMT_LEFT = 0;
private static final int LVCFMT_RIGHT = 1;
private static final int LVCFMT_CENTER = 2;
@cli.System.Security.SecuritySafeCriticalAttribute.Annotation
public ShellFolderColumnInfo[] getFolderColumns() {
Object o = doGetColumnInfo(getIShellFolder());
ShellFolderColumnInfo[] columns = (ShellFolderColumnInfo[]) o;
if (columns != null) {
List