0001: /*
0002: * Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package sun.awt.shell;
0027:
0028: import java.awt.Image;
0029: import java.awt.Toolkit;
0030: import java.awt.image.BufferedImage;
0031: import java.io.File;
0032: import java.io.FileNotFoundException;
0033: import java.io.IOException;
0034: import java.util.*;
0035: import javax.swing.SwingConstants;
0036:
0037: // NOTE: This class supersedes Win32ShellFolder, which was removed from
0038: // distribution after version 1.4.2.
0039:
0040: /**
0041: * Win32 Shell Folders
0042: * <P>
0043: * <BR>
0044: * There are two fundamental types of shell folders : file system folders
0045: * and non-file system folders. File system folders are relatively easy
0046: * to deal with. Non-file system folders are items such as My Computer,
0047: * Network Neighborhood, and the desktop. Some of these non-file system
0048: * folders have special values and properties.
0049: * <P>
0050: * <BR>
0051: * Win32 keeps two basic data structures for shell folders. The first
0052: * of these is called an ITEMIDLIST. Usually a pointer, called an
0053: * LPITEMIDLIST, or more frequently just "PIDL". This structure holds
0054: * a series of identifiers and can be either relative to the desktop
0055: * (an absolute PIDL), or relative to the shell folder that contains them.
0056: * Some Win32 functions can take absolute or relative PIDL values, and
0057: * others can only accept relative values.
0058: * <BR>
0059: * The second data structure is an IShellFolder COM interface. Using
0060: * this interface, one can enumerate the relative PIDLs in a shell
0061: * folder, get attributes, etc.
0062: * <BR>
0063: * All Win32ShellFolder2 objects which are folder types (even non-file
0064: * system folders) contain an IShellFolder object. Files are named in
0065: * directories via relative PIDLs.
0066: *
0067: * @author Michael Martak
0068: * @author Leif Samuelsson
0069: * @author Kenneth Russell
0070: * @since 1.4 */
0071:
0072: final class Win32ShellFolder2 extends ShellFolder {
0073:
0074: private static native void initIDs();
0075:
0076: private static final boolean is98;
0077:
0078: static {
0079: String osName = System.getProperty("os.name");
0080: is98 = (osName != null && osName.startsWith("Windows 98"));
0081:
0082: initIDs();
0083: }
0084:
0085: // Win32 Shell Folder Constants
0086: public static final int DESKTOP = 0x0000;
0087: public static final int INTERNET = 0x0001;
0088: public static final int PROGRAMS = 0x0002;
0089: public static final int CONTROLS = 0x0003;
0090: public static final int PRINTERS = 0x0004;
0091: public static final int PERSONAL = 0x0005;
0092: public static final int FAVORITES = 0x0006;
0093: public static final int STARTUP = 0x0007;
0094: public static final int RECENT = 0x0008;
0095: public static final int SENDTO = 0x0009;
0096: public static final int BITBUCKET = 0x000a;
0097: public static final int STARTMENU = 0x000b;
0098: public static final int DESKTOPDIRECTORY = 0x0010;
0099: public static final int DRIVES = 0x0011;
0100: public static final int NETWORK = 0x0012;
0101: public static final int NETHOOD = 0x0013;
0102: public static final int FONTS = 0x0014;
0103: public static final int TEMPLATES = 0x0015;
0104: public static final int COMMON_STARTMENU = 0x0016;
0105: public static final int COMMON_PROGRAMS = 0X0017;
0106: public static final int COMMON_STARTUP = 0x0018;
0107: public static final int COMMON_DESKTOPDIRECTORY = 0x0019;
0108: public static final int APPDATA = 0x001a;
0109: public static final int PRINTHOOD = 0x001b;
0110: public static final int ALTSTARTUP = 0x001d;
0111: public static final int COMMON_ALTSTARTUP = 0x001e;
0112: public static final int COMMON_FAVORITES = 0x001f;
0113: public static final int INTERNET_CACHE = 0x0020;
0114: public static final int COOKIES = 0x0021;
0115: public static final int HISTORY = 0x0022;
0116:
0117: // Win32 shell folder attributes
0118: public static final int ATTRIB_CANCOPY = 0x00000001;
0119: public static final int ATTRIB_CANMOVE = 0x00000002;
0120: public static final int ATTRIB_CANLINK = 0x00000004;
0121: public static final int ATTRIB_CANRENAME = 0x00000010;
0122: public static final int ATTRIB_CANDELETE = 0x00000020;
0123: public static final int ATTRIB_HASPROPSHEET = 0x00000040;
0124: public static final int ATTRIB_DROPTARGET = 0x00000100;
0125: public static final int ATTRIB_LINK = 0x00010000;
0126: public static final int ATTRIB_SHARE = 0x00020000;
0127: public static final int ATTRIB_READONLY = 0x00040000;
0128: public static final int ATTRIB_GHOSTED = 0x00080000;
0129: public static final int ATTRIB_HIDDEN = 0x00080000;
0130: public static final int ATTRIB_FILESYSANCESTOR = 0x10000000;
0131: public static final int ATTRIB_FOLDER = 0x20000000;
0132: public static final int ATTRIB_FILESYSTEM = 0x40000000;
0133: public static final int ATTRIB_HASSUBFOLDER = 0x80000000;
0134: public static final int ATTRIB_VALIDATE = 0x01000000;
0135: public static final int ATTRIB_REMOVABLE = 0x02000000;
0136: public static final int ATTRIB_COMPRESSED = 0x04000000;
0137: public static final int ATTRIB_BROWSABLE = 0x08000000;
0138: public static final int ATTRIB_NONENUMERATED = 0x00100000;
0139: public static final int ATTRIB_NEWCONTENT = 0x00200000;
0140:
0141: // IShellFolder::GetDisplayNameOf constants
0142: public static final int SHGDN_NORMAL = 0;
0143: public static final int SHGDN_INFOLDER = 1;
0144: public static final int SHGDN_INCLUDE_NONFILESYS = 0x2000;
0145: public static final int SHGDN_FORADDRESSBAR = 0x4000;
0146: public static final int SHGDN_FORPARSING = 0x8000;
0147:
0148: // Values for system call LoadIcon()
0149: public enum SystemIcon {
0150: IDI_APPLICATION(32512), IDI_HAND(32513), IDI_ERROR(32513), IDI_QUESTION(
0151: 32514), IDI_EXCLAMATION(32515), IDI_WARNING(32515), IDI_ASTERISK(
0152: 32516), IDI_INFORMATION(32516), IDI_WINLOGO(32517);
0153:
0154: private final int iconID;
0155:
0156: SystemIcon(int iconID) {
0157: this .iconID = iconID;
0158: }
0159:
0160: public int getIconID() {
0161: return iconID;
0162: }
0163: }
0164:
0165: static class FolderDisposer implements sun.java2d.DisposerRecord {
0166: /*
0167: * This is cached as a concession to getFolderType(), which needs
0168: * an absolute PIDL.
0169: */
0170: long absolutePIDL;
0171: /*
0172: * We keep track of shell folders through the IShellFolder
0173: * interface of their parents plus their relative PIDL.
0174: */
0175: long pIShellFolder;
0176: long relativePIDL;
0177:
0178: boolean disposed;
0179:
0180: public void dispose() {
0181: if (disposed)
0182: return;
0183: if (relativePIDL != 0) {
0184: releasePIDL(relativePIDL);
0185: }
0186: if (absolutePIDL != 0) {
0187: releasePIDL(absolutePIDL);
0188: }
0189: if (pIShellFolder != 0) {
0190: releaseIShellFolder(pIShellFolder);
0191: }
0192: disposed = true;
0193: }
0194: }
0195:
0196: FolderDisposer disposer = new FolderDisposer();
0197:
0198: private void setIShellFolder(long pIShellFolder) {
0199: disposer.pIShellFolder = pIShellFolder;
0200: }
0201:
0202: private void setRelativePIDL(long relativePIDL) {
0203: disposer.relativePIDL = relativePIDL;
0204: }
0205:
0206: /*
0207: * The following are for caching various shell folder properties.
0208: */
0209: private long pIShellIcon = -1L;
0210: private String folderType = null;
0211: private String displayName = null;
0212: private Image smallIcon = null;
0213: private Image largeIcon = null;
0214: private Boolean isDir = null;
0215:
0216: /*
0217: * The following is to identify the My Documents folder as being special
0218: */
0219: private boolean isPersonal;
0220:
0221: /**
0222: * Create a system special shell folder, such as the
0223: * desktop or Network Neighborhood.
0224: */
0225: Win32ShellFolder2(int csidl) throws IOException {
0226: // Desktop is parent of DRIVES and NETWORK, not necessarily
0227: // other special shell folders.
0228: super (
0229: null,
0230: (getFileSystemPath(csidl) == null) ? ("ShellFolder: 0x" + Integer
0231: .toHexString(csidl))
0232: : getFileSystemPath(csidl));
0233: if (csidl == DESKTOP) {
0234: initDesktop();
0235: } else {
0236: initSpecial(getDesktop().getIShellFolder(), csidl);
0237: // At this point, the native method initSpecial() has set our relativePIDL
0238: // relative to the Desktop, which may not be our immediate parent. We need
0239: // to traverse this ID list and break it into a chain of shell folders from
0240: // the top, with each one having an immediate parent and a relativePIDL
0241: // relative to that parent.
0242: long pIDL = disposer.relativePIDL;
0243: parent = getDesktop();
0244: while (pIDL != 0) {
0245: // Get a child pidl relative to 'parent'
0246: long childPIDL = copyFirstPIDLEntry(pIDL);
0247: if (childPIDL != 0) {
0248: // Get a handle to the the rest of the ID list
0249: // i,e, parent's grandchilren and down
0250: pIDL = getNextPIDLEntry(pIDL);
0251: if (pIDL != 0) {
0252: // Now we know that parent isn't immediate to 'this' because it
0253: // has a continued ID list. Create a shell folder for this child
0254: // pidl and make it the new 'parent'.
0255: parent = new Win32ShellFolder2(
0256: (Win32ShellFolder2) parent, childPIDL);
0257: } else {
0258: // No grandchildren means we have arrived at the parent of 'this',
0259: // and childPIDL is directly relative to parent.
0260: disposer.relativePIDL = childPIDL;
0261: }
0262: } else {
0263: break;
0264: }
0265: }
0266: }
0267:
0268: sun.java2d.Disposer.addRecord(this , disposer);
0269: }
0270:
0271: /**
0272: * Create a system shell folder
0273: */
0274: Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder,
0275: long relativePIDL, String path) {
0276: super (parent, (path != null) ? path : "ShellFolder: ");
0277: this .disposer.pIShellFolder = pIShellFolder;
0278: this .disposer.relativePIDL = relativePIDL;
0279: sun.java2d.Disposer.addRecord(this , disposer);
0280: }
0281:
0282: /**
0283: * Creates a shell folder with a parent and relative PIDL
0284: */
0285: Win32ShellFolder2(Win32ShellFolder2 parent, long relativePIDL) {
0286: super (parent, getFileSystemPath(parent.getIShellFolder(),
0287: relativePIDL));
0288: this .disposer.relativePIDL = relativePIDL;
0289: getAbsolutePath();
0290: sun.java2d.Disposer.addRecord(this , disposer);
0291: }
0292:
0293: // Initializes the desktop shell folder
0294: private native void initDesktop();
0295:
0296: // Initializes a special, non-file system shell folder
0297: // from one of the above constants
0298: private native void initSpecial(long desktopIShellFolder, int csidl);
0299:
0300: /** Marks this folder as being the My Documents (Personal) folder */
0301: public void setIsPersonal() {
0302: isPersonal = true;
0303: }
0304:
0305: /**
0306: * This method is implemented to make sure that no instances
0307: * of <code>ShellFolder</code> are ever serialized. If <code>isFileSystem()</code> returns
0308: * <code>true</code>, then the object is representable with an instance of
0309: * <code>java.io.File</code> instead. If not, then the object depends
0310: * on native PIDL state and should not be serialized.
0311: *
0312: * @returns a <code>java.io.File</code> replacement object. If the folder
0313: * is a not a normal directory, then returns the first non-removable
0314: * drive (normally "C:\").
0315: */
0316: protected Object writeReplace()
0317: throws java.io.ObjectStreamException {
0318: if (isFileSystem()) {
0319: return new File(getPath());
0320: } else {
0321: Win32ShellFolder2 drives = Win32ShellFolderManager2
0322: .getDrives();
0323: if (drives != null) {
0324: File[] driveRoots = drives.listFiles();
0325: if (driveRoots != null) {
0326: for (int i = 0; i < driveRoots.length; i++) {
0327: if (driveRoots[i] instanceof Win32ShellFolder2) {
0328: Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];
0329: if (sf.isFileSystem()
0330: && !sf
0331: .hasAttribute(ATTRIB_REMOVABLE)) {
0332: return new File(sf.getPath());
0333: }
0334: }
0335: }
0336: }
0337: }
0338: // Ouch, we have no hard drives. Return something "valid" anyway.
0339: return new File("C:\\");
0340: }
0341: }
0342:
0343: /**
0344: * Finalizer to clean up any COM objects or PIDLs used by this object.
0345: */
0346: protected void dispose() {
0347: disposer.dispose();
0348: }
0349:
0350: // Given a (possibly multi-level) relative PIDL (with respect to
0351: // the desktop, at least in all of the usage cases in this code),
0352: // return a pointer to the next entry. Does not mutate the PIDL in
0353: // any way. Returns 0 if the null terminator is reached.
0354: // Needs to be accessible to Win32ShellFolderManager2
0355: static native long getNextPIDLEntry(long pIDL);
0356:
0357: // Given a (possibly multi-level) relative PIDL (with respect to
0358: // the desktop, at least in all of the usage cases in this code),
0359: // copy the first entry into a newly-allocated PIDL. Returns 0 if
0360: // the PIDL is at the end of the list.
0361: // Needs to be accessible to Win32ShellFolderManager2
0362: static native long copyFirstPIDLEntry(long pIDL);
0363:
0364: // Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL
0365: private static native long combinePIDLs(long ppIDL, long pIDL);
0366:
0367: // Release a PIDL object
0368: // Needs to be accessible to Win32ShellFolderManager2
0369: static native void releasePIDL(long pIDL);
0370:
0371: // Release an IShellFolder object
0372: private static native void releaseIShellFolder(long pIShellFolder);
0373:
0374: /**
0375: * Accessor for IShellFolder
0376: */
0377: public long getIShellFolder() {
0378: if (disposer.pIShellFolder == 0) {
0379: assert (isDirectory());
0380: assert (parent != null);
0381: long parentIShellFolder = getParentIShellFolder();
0382: if (parentIShellFolder == 0) {
0383: throw new InternalError(
0384: "Parent IShellFolder was null for "
0385: + getAbsolutePath());
0386: }
0387: // We are a directory with a parent and a relative PIDL.
0388: // We want to bind to the parent so we get an IShellFolder instance associated with us.
0389: disposer.pIShellFolder = bindToObject(parentIShellFolder,
0390: disposer.relativePIDL);
0391: if (disposer.pIShellFolder == 0) {
0392: throw new InternalError("Unable to bind "
0393: + getAbsolutePath() + " to parent");
0394: }
0395: }
0396: return disposer.pIShellFolder;
0397: }
0398:
0399: /**
0400: * Get the parent ShellFolder's IShellFolder interface
0401: */
0402: public long getParentIShellFolder() {
0403: Win32ShellFolder2 parent = (Win32ShellFolder2) getParentFile();
0404: if (parent == null) {
0405: // Parent should only be null if this is the desktop, whose
0406: // relativePIDL is relative to its own IShellFolder.
0407: return getIShellFolder();
0408: }
0409: return parent.getIShellFolder();
0410: }
0411:
0412: /**
0413: * Accessor for relative PIDL
0414: */
0415: public long getRelativePIDL() {
0416: if (disposer.relativePIDL == 0) {
0417: throw new InternalError(
0418: "Should always have a relative PIDL");
0419: }
0420: return disposer.relativePIDL;
0421: }
0422:
0423: private long getAbsolutePIDL() {
0424: if (parent == null) {
0425: // This is the desktop
0426: return getRelativePIDL();
0427: } else {
0428: if (disposer.absolutePIDL == 0) {
0429: disposer.absolutePIDL = combinePIDLs(
0430: ((Win32ShellFolder2) parent).getAbsolutePIDL(),
0431: getRelativePIDL());
0432: }
0433:
0434: return disposer.absolutePIDL;
0435: }
0436: }
0437:
0438: /**
0439: * Helper function to return the desktop
0440: */
0441: public Win32ShellFolder2 getDesktop() {
0442: return Win32ShellFolderManager2.getDesktop();
0443: }
0444:
0445: /**
0446: * Helper function to return the desktop IShellFolder interface
0447: */
0448: public long getDesktopIShellFolder() {
0449: return getDesktop().getIShellFolder();
0450: }
0451:
0452: private static boolean pathsEqual(String path1, String path2) {
0453: // Same effective implementation as Win32FileSystem
0454: return path1.equalsIgnoreCase(path2);
0455: }
0456:
0457: /**
0458: * Check to see if two ShellFolder objects are the same
0459: */
0460: public boolean equals(Object o) {
0461: if (o == null || !(o instanceof Win32ShellFolder2)) {
0462: // Short-circuit circuitous delegation path
0463: if (!(o instanceof File)) {
0464: return super .equals(o);
0465: }
0466: return pathsEqual(getPath(), ((File) o).getPath());
0467: }
0468: Win32ShellFolder2 rhs = (Win32ShellFolder2) o;
0469: if ((parent == null && rhs.parent != null)
0470: || (parent != null && rhs.parent == null)) {
0471: return false;
0472: }
0473:
0474: if (isFileSystem() && rhs.isFileSystem()) {
0475: // Only folders with identical parents can be equal
0476: return (pathsEqual(getPath(), rhs.getPath()) && (parent == rhs.parent || parent
0477: .equals(rhs.parent)));
0478: }
0479:
0480: if (parent == rhs.parent || parent.equals(rhs.parent)) {
0481: return pidlsEqual(getParentIShellFolder(),
0482: disposer.relativePIDL, rhs.disposer.relativePIDL);
0483: }
0484:
0485: return false;
0486: }
0487:
0488: private static boolean pidlsEqual(long pIShellFolder, long pidl1,
0489: long pidl2) {
0490: return (compareIDs(pIShellFolder, pidl1, pidl2) == 0);
0491: }
0492:
0493: private static native int compareIDs(long pParentIShellFolder,
0494: long pidl1, long pidl2);
0495:
0496: /**
0497: * @return Whether this is a file system shell folder
0498: */
0499: public boolean isFileSystem() {
0500: return hasAttribute(ATTRIB_FILESYSTEM);
0501: }
0502:
0503: /**
0504: * Return whether the given attribute flag is set for this object
0505: */
0506: public boolean hasAttribute(int attribute) {
0507: // Caching at this point doesn't seem to be cost efficient
0508: return (getAttributes0(getParentIShellFolder(),
0509: getRelativePIDL(), attribute) & attribute) != 0;
0510: }
0511:
0512: /**
0513: * Returns the queried attributes specified in attrsMask.
0514: *
0515: * Could plausibly be used for attribute caching but have to be
0516: * very careful not to touch network drives and file system roots
0517: * with a full attrsMask
0518: */
0519: private static native int getAttributes0(long pParentIShellFolder,
0520: long pIDL, int attrsMask);
0521:
0522: // Return the path to the underlying file system object
0523: private static String getFileSystemPath(long parentIShellFolder,
0524: long relativePIDL) {
0525: int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
0526: if (parentIShellFolder == Win32ShellFolderManager2.getNetwork()
0527: .getIShellFolder()
0528: && getAttributes0(parentIShellFolder, relativePIDL,
0529: linkedFolder) == linkedFolder) {
0530:
0531: String s = getFileSystemPath(Win32ShellFolderManager2
0532: .getDesktop().getIShellFolder(), getLinkLocation(
0533: parentIShellFolder, relativePIDL, false));
0534: if (s != null && s.startsWith("\\\\")) {
0535: return s;
0536: }
0537: }
0538: return getDisplayNameOf(parentIShellFolder, relativePIDL,
0539: SHGDN_NORMAL | SHGDN_FORPARSING);
0540: }
0541:
0542: // Needs to be accessible to Win32ShellFolderManager2
0543: static native String getFileSystemPath(int csidl)
0544: throws IOException;
0545:
0546: // Return whether the path is a network root.
0547: // Path is assumed to be non-null
0548: private static boolean isNetworkRoot(String path) {
0549: return (path.equals("\\\\") || path.equals("\\")
0550: || path.equals("//") || path.equals("/"));
0551: }
0552:
0553: /**
0554: * @return The parent shell folder of this shell folder, null if
0555: * there is no parent
0556: */
0557: public File getParentFile() {
0558: return parent;
0559: }
0560:
0561: public boolean isDirectory() {
0562: if (isDir == null) {
0563: // Folders with SFGAO_BROWSABLE have "shell extension" handlers and are
0564: // not traversable in JFileChooser. An exception is "My Documents" on
0565: // Windows 98.
0566: if ((hasAttribute(ATTRIB_HASSUBFOLDER) || hasAttribute(ATTRIB_FOLDER))
0567: && (!hasAttribute(ATTRIB_BROWSABLE) || (is98 && equals(Win32ShellFolderManager2
0568: .getPersonal())))) {
0569: isDir = Boolean.TRUE;
0570: } else if (isLink()) {
0571: ShellFolder linkLocation = getLinkLocation(false);
0572: isDir = Boolean.valueOf(linkLocation != null
0573: && linkLocation.isDirectory());
0574: } else {
0575: isDir = Boolean.FALSE;
0576: }
0577: }
0578: return isDir.booleanValue();
0579: }
0580:
0581: /*
0582: * Functions for enumerating an IShellFolder's children
0583: */
0584: // Returns an IEnumIDList interface for an IShellFolder. The value
0585: // returned must be released using releaseEnumObjects().
0586: private long getEnumObjects(long pIShellFolder,
0587: boolean includeHiddenFiles) {
0588: boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder());
0589: return getEnumObjects(disposer.pIShellFolder, isDesktop,
0590: includeHiddenFiles);
0591: }
0592:
0593: // Returns an IEnumIDList interface for an IShellFolder. The value
0594: // returned must be released using releaseEnumObjects().
0595: private native long getEnumObjects(long pIShellFolder,
0596: boolean isDesktop, boolean includeHiddenFiles);
0597:
0598: // Returns the next sequential child as a relative PIDL
0599: // from an IEnumIDList interface. The value returned must
0600: // be released using releasePIDL().
0601: private native long getNextChild(long pEnumObjects);
0602:
0603: // Releases the IEnumIDList interface
0604: private native void releaseEnumObjects(long pEnumObjects);
0605:
0606: // Returns the IShellFolder of a child from a parent IShellFolder
0607: // and a relative PIDL. The value returned must be released
0608: // using releaseIShellFolder().
0609: private static native long bindToObject(long parentIShellFolder,
0610: long pIDL);
0611:
0612: /**
0613: * @return An array of shell folders that are children of this shell folder
0614: * object. The array will be empty if the folder is empty. Returns
0615: * <code>null</code> if this shellfolder does not denote a directory.
0616: */
0617: public File[] listFiles(boolean includeHiddenFiles) {
0618: if (!isDirectory()) {
0619: return null;
0620: }
0621: // Links to directories are not directories and cannot be parents.
0622: // This does not apply to folders in My Network Places (NetHood)
0623: // because they are both links and real directories!
0624: if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
0625: return new File[0];
0626: }
0627:
0628: Win32ShellFolder2 desktop = Win32ShellFolderManager2
0629: .getDesktop();
0630: Win32ShellFolder2 personal = Win32ShellFolderManager2
0631: .getPersonal();
0632:
0633: // If we are a directory, we have a parent and (at least) a
0634: // relative PIDL. We must first ensure we are bound to the
0635: // parent so we have an IShellFolder to query.
0636: long pIShellFolder = getIShellFolder();
0637: // Now we can enumerate the objects in this folder.
0638: ArrayList list = new ArrayList();
0639: long pEnumObjects = getEnumObjects(pIShellFolder,
0640: includeHiddenFiles);
0641: if (pEnumObjects != 0) {
0642: long childPIDL = 0;
0643: int testedAttrs = ATTRIB_FILESYSTEM
0644: | ATTRIB_FILESYSANCESTOR;
0645: do {
0646: if (Thread.currentThread().isInterrupted()) {
0647: return new File[0];
0648: }
0649: childPIDL = getNextChild(pEnumObjects);
0650: boolean releasePIDL = true;
0651: if (childPIDL != 0
0652: && (getAttributes0(pIShellFolder, childPIDL,
0653: testedAttrs) & testedAttrs) != 0) {
0654: Win32ShellFolder2 childFolder = null;
0655: if (this .equals(desktop)
0656: && personal != null
0657: && pidlsEqual(pIShellFolder, childPIDL,
0658: personal.disposer.relativePIDL)) {
0659: childFolder = personal;
0660: } else {
0661: childFolder = new Win32ShellFolder2(this ,
0662: childPIDL);
0663: releasePIDL = false;
0664: }
0665: list.add(childFolder);
0666: }
0667: if (releasePIDL) {
0668: releasePIDL(childPIDL);
0669: }
0670: } while (childPIDL != 0);
0671: releaseEnumObjects(pEnumObjects);
0672: }
0673: return (ShellFolder[]) list
0674: .toArray(new ShellFolder[list.size()]);
0675: }
0676:
0677: /**
0678: * Look for (possibly special) child folder by it's path
0679: *
0680: * @return The child shellfolder, or null if not found.
0681: */
0682: Win32ShellFolder2 getChildByPath(String filePath) {
0683: long pIShellFolder = getIShellFolder();
0684: long pEnumObjects = getEnumObjects(pIShellFolder, true);
0685: Win32ShellFolder2 child = null;
0686: long childPIDL = 0;
0687:
0688: while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
0689: if (getAttributes0(pIShellFolder, childPIDL,
0690: ATTRIB_FILESYSTEM) != 0) {
0691: String path = getFileSystemPath(pIShellFolder,
0692: childPIDL);
0693: if (path != null && path.equalsIgnoreCase(filePath)) {
0694: long childIShellFolder = bindToObject(
0695: pIShellFolder, childPIDL);
0696: child = new Win32ShellFolder2(this ,
0697: childIShellFolder, childPIDL, path);
0698: break;
0699: }
0700: }
0701: releasePIDL(childPIDL);
0702: }
0703: releaseEnumObjects(pEnumObjects);
0704: return child;
0705: }
0706:
0707: /**
0708: * @return Whether this shell folder is a link
0709: */
0710: public boolean isLink() {
0711: return hasAttribute(ATTRIB_LINK);
0712: }
0713:
0714: /**
0715: * @return Whether this shell folder is marked as hidden
0716: */
0717: public boolean isHidden() {
0718: return hasAttribute(ATTRIB_HIDDEN);
0719: }
0720:
0721: // Return the link location of a shell folder
0722: private static native long getLinkLocation(long parentIShellFolder,
0723: long relativePIDL, boolean resolve);
0724:
0725: /**
0726: * @return The shell folder linked to by this shell folder, or null
0727: * if this shell folder is not a link or is a broken or invalid link
0728: */
0729: public ShellFolder getLinkLocation() {
0730: return getLinkLocation(true);
0731: }
0732:
0733: private ShellFolder getLinkLocation(boolean resolve) {
0734: if (!isLink()) {
0735: return null;
0736: }
0737:
0738: ShellFolder location = null;
0739: long linkLocationPIDL = getLinkLocation(
0740: getParentIShellFolder(), getRelativePIDL(), resolve);
0741: if (linkLocationPIDL != 0) {
0742: try {
0743: location = Win32ShellFolderManager2
0744: .createShellFolderFromRelativePIDL(
0745: getDesktop(), linkLocationPIDL);
0746: } catch (InternalError e) {
0747: // Could be a link to a non-bindable object, such as a network connection
0748: // TODO: getIShellFolder() should throw FileNotFoundException instead
0749: }
0750: }
0751: return location;
0752: }
0753:
0754: // Parse a display name into a PIDL relative to the current IShellFolder.
0755: long parseDisplayName(String name) throws FileNotFoundException {
0756: try {
0757: return parseDisplayName0(getIShellFolder(), name);
0758: } catch (IOException e) {
0759: throw new FileNotFoundException("Could not find file "
0760: + name);
0761: }
0762: }
0763:
0764: private static native long parseDisplayName0(long pIShellFolder,
0765: String name) throws IOException;
0766:
0767: // Return the display name of a shell folder
0768: private static native String getDisplayNameOf(
0769: long parentIShellFolder, long relativePIDL, int attrs);
0770:
0771: /**
0772: * @return The name used to display this shell folder
0773: */
0774: public String getDisplayName() {
0775: if (displayName == null) {
0776: displayName = getDisplayNameOf(getParentIShellFolder(),
0777: getRelativePIDL(), SHGDN_NORMAL);
0778: }
0779: return displayName;
0780: }
0781:
0782: // Return the folder type of a shell folder
0783: private static native String getFolderType(long pIDL);
0784:
0785: /**
0786: * @return The type of shell folder as a string
0787: */
0788: public String getFolderType() {
0789: if (folderType == null) {
0790: folderType = getFolderType(getAbsolutePIDL());
0791: }
0792: return folderType;
0793: }
0794:
0795: // Return the executable type of a file system shell folder
0796: private native String getExecutableType(String path);
0797:
0798: /**
0799: * @return The executable type as a string
0800: */
0801: public String getExecutableType() {
0802: if (!isFileSystem()) {
0803: return null;
0804: }
0805: return getExecutableType(getAbsolutePath());
0806: }
0807:
0808: // Icons
0809:
0810: private static Map smallSystemImages = new HashMap();
0811: private static Map largeSystemImages = new HashMap();
0812: private static Map smallLinkedSystemImages = new HashMap();
0813: private static Map largeLinkedSystemImages = new HashMap();
0814:
0815: private static native long getIShellIcon(long pIShellFolder);
0816:
0817: private static native int getIconIndex(long parentIShellIcon,
0818: long relativePIDL);
0819:
0820: // Return the icon of a file system shell folder in the form of an HICON
0821: private static native long getIcon(String absolutePath,
0822: boolean getLargeIcon);
0823:
0824: private static native long extractIcon(long parentIShellFolder,
0825: long relativePIDL, boolean getLargeIcon);
0826:
0827: // Returns an icon from the Windows system icon list in the form of an HICON
0828: private static native long getSystemIcon(int iconID);
0829:
0830: private static native long getIconResource(String libName,
0831: int iconID, int cxDesired, int cyDesired,
0832: boolean useVGAColors);
0833:
0834: // Note: useVGAColors is ignored on XP and later
0835:
0836: // Return the bits from an HICON. This has a side effect of setting
0837: // the imageHash variable for efficient caching / comparing.
0838: private static native int[] getIconBits(long hIcon, int iconSize);
0839:
0840: // Dispose the HICON
0841: private static native void disposeIcon(long hIcon);
0842:
0843: public static native int[] getFileChooserBitmapBits();
0844:
0845: private long getIShellIcon() {
0846: if (pIShellIcon == -1L) {
0847: pIShellIcon = getIShellIcon(getIShellFolder());
0848: }
0849: return pIShellIcon;
0850: }
0851:
0852: static int[] fileChooserBitmapBits = null;
0853: static Image[] fileChooserIcons = new Image[47];
0854:
0855: static Image getFileChooserIcon(int i) {
0856: if (fileChooserIcons[i] != null) {
0857: return fileChooserIcons[i];
0858: } else {
0859: if (fileChooserBitmapBits == null) {
0860: fileChooserBitmapBits = getFileChooserBitmapBits();
0861: }
0862: if (fileChooserBitmapBits != null) {
0863: int nImages = fileChooserBitmapBits.length / (16 * 16);
0864: int[] bitmapBits = new int[16 * 16];
0865: for (int y = 0; y < 16; y++) {
0866: for (int x = 0; x < 16; x++) {
0867: bitmapBits[y * 16 + x] = fileChooserBitmapBits[y
0868: * (nImages * 16) + (i * 16) + x];
0869: }
0870: }
0871: BufferedImage img = new BufferedImage(16, 16,
0872: BufferedImage.TYPE_INT_ARGB);
0873: img.setRGB(0, 0, 16, 16, bitmapBits, 0, 16);
0874: fileChooserIcons[i] = img;
0875: }
0876: }
0877: return fileChooserIcons[i];
0878: }
0879:
0880: private static Image makeIcon(long hIcon, boolean getLargeIcon) {
0881: if (hIcon != 0L && hIcon != -1L) {
0882: // Get the bits. This has the side effect of setting the imageHash value for this object.
0883: int size = getLargeIcon ? 32 : 16;
0884: int[] iconBits = getIconBits(hIcon, size);
0885: if (iconBits != null) {
0886: BufferedImage img = new BufferedImage(size, size,
0887: BufferedImage.TYPE_INT_ARGB);
0888: img.setRGB(0, 0, size, size, iconBits, 0, size);
0889: return img;
0890: }
0891: }
0892: return null;
0893: }
0894:
0895: /**
0896: * @return The icon image used to display this shell folder
0897: */
0898: public Image getIcon(boolean getLargeIcon) {
0899: Image icon = getLargeIcon ? largeIcon : smallIcon;
0900: if (icon == null) {
0901: long parentIShellIcon = (parent != null) ? ((Win32ShellFolder2) parent)
0902: .getIShellIcon()
0903: : 0L;
0904: long relativePIDL = getRelativePIDL();
0905:
0906: if (isFileSystem()) {
0907: // These are cached per type (using the index in the system image list)
0908: int index = getIconIndex(parentIShellIcon, relativePIDL);
0909: if (index > 0) {
0910: Map imageCache;
0911: if (isLink()) {
0912: imageCache = getLargeIcon ? largeLinkedSystemImages
0913: : smallLinkedSystemImages;
0914: } else {
0915: imageCache = getLargeIcon ? largeSystemImages
0916: : smallSystemImages;
0917: }
0918: icon = (Image) imageCache.get(Integer
0919: .valueOf(index));
0920: if (icon == null) {
0921: long hIcon = getIcon(getAbsolutePath(),
0922: getLargeIcon);
0923: icon = makeIcon(hIcon, getLargeIcon);
0924: disposeIcon(hIcon);
0925: if (icon != null) {
0926: imageCache
0927: .put(Integer.valueOf(index), icon);
0928: }
0929: }
0930: }
0931: }
0932:
0933: if (icon == null) {
0934: // These are only cached per object
0935: long hIcon = extractIcon(getParentIShellFolder(),
0936: getRelativePIDL(), getLargeIcon);
0937: icon = makeIcon(hIcon, getLargeIcon);
0938: disposeIcon(hIcon);
0939: }
0940:
0941: if (getLargeIcon) {
0942: largeIcon = icon;
0943: } else {
0944: smallIcon = icon;
0945: }
0946: }
0947: if (icon == null) {
0948: icon = super .getIcon(getLargeIcon);
0949: }
0950: return icon;
0951: }
0952:
0953: /**
0954: * Gets an icon from the Windows system icon list as an <code>Image</code>
0955: */
0956: static Image getSystemIcon(SystemIcon iconType) {
0957: long hIcon = getSystemIcon(iconType.getIconID());
0958: Image icon = makeIcon(hIcon, true);
0959: disposeIcon(hIcon);
0960: return icon;
0961: }
0962:
0963: /**
0964: * Gets an icon from the Windows system icon list as an <code>Image</code>
0965: */
0966: static Image getShell32Icon(int iconID) {
0967: boolean useVGAColors = true; // Will be ignored on XP and later
0968:
0969: Toolkit toolkit = Toolkit.getDefaultToolkit();
0970: String shellIconBPP = (String) toolkit
0971: .getDesktopProperty("win.icon.shellIconBPP");
0972: if (shellIconBPP != null) {
0973: useVGAColors = shellIconBPP.equals("4");
0974: }
0975:
0976: long hIcon = getIconResource("shell32.dll", iconID, 16, 16,
0977: useVGAColors);
0978: if (hIcon != 0) {
0979: Image icon = makeIcon(hIcon, false);
0980: disposeIcon(hIcon);
0981: return icon;
0982: }
0983: return null;
0984: }
0985:
0986: /**
0987: * Returns the canonical form of this abstract pathname. Equivalent to
0988: * <code>new Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())</code>.
0989: *
0990: * @see java.io.File#getCanonicalFile
0991: */
0992: public File getCanonicalFile() throws IOException {
0993: return this ;
0994: }
0995:
0996: /*
0997: * Indicates whether this is a special folder (includes My Documents)
0998: */
0999: public boolean isSpecial() {
1000: return isPersonal || !isFileSystem() || (this == getDesktop());
1001: }
1002:
1003: /**
1004: * Compares this object with the specified object for order.
1005: *
1006: * @see sun.awt.shell.ShellFolder#compareTo(File)
1007: */
1008: public int compareTo(File file2) {
1009: if (!(file2 instanceof Win32ShellFolder2)) {
1010: if (isFileSystem() && !isSpecial()) {
1011: return super .compareTo(file2);
1012: } else {
1013: return -1; // Non-file shellfolders sort before files
1014: }
1015: }
1016: return Win32ShellFolderManager2.compareShellFolders(this ,
1017: (Win32ShellFolder2) file2);
1018: }
1019:
1020: // native constants from commctrl.h
1021: private static final int LVCFMT_LEFT = 0;
1022: private static final int LVCFMT_RIGHT = 1;
1023: private static final int LVCFMT_CENTER = 2;
1024:
1025: public ShellFolderColumnInfo[] getFolderColumns() {
1026: ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
1027:
1028: if (columns != null) {
1029: List<ShellFolderColumnInfo> notNullColumns = new ArrayList<ShellFolderColumnInfo>();
1030: for (int i = 0; i < columns.length; i++) {
1031: ShellFolderColumnInfo column = columns[i];
1032: if (column != null) {
1033: column
1034: .setAlignment(column.getAlignment() == LVCFMT_RIGHT ? SwingConstants.RIGHT
1035: : column.getAlignment() == LVCFMT_CENTER ? SwingConstants.CENTER
1036: : SwingConstants.LEADING);
1037:
1038: column.setComparator(new ColumnComparator(i));
1039:
1040: notNullColumns.add(column);
1041: }
1042: }
1043: columns = new ShellFolderColumnInfo[notNullColumns.size()];
1044: notNullColumns.toArray(columns);
1045: }
1046: return columns;
1047: }
1048:
1049: public Object getFolderColumnValue(int column) {
1050: return doGetColumnValue(getParentIShellFolder(),
1051: getRelativePIDL(), column);
1052: }
1053:
1054: private native ShellFolderColumnInfo[] doGetColumnInfo(
1055: long iShellFolder2);
1056:
1057: private native Object doGetColumnValue(long parentIShellFolder2,
1058: long childPIDL, int columnIdx);
1059:
1060: private native int compareIDsByColumn(long pParentIShellFolder,
1061: long pidl1, long pidl2, int columnIdx);
1062:
1063: private class ColumnComparator implements Comparator {
1064: private final int columnIdx;
1065:
1066: public ColumnComparator(int columnIdx) {
1067: this .columnIdx = columnIdx;
1068: }
1069:
1070: // compares 2 objects within this folder by the specified column
1071: public int compare(Object o, Object o1) {
1072: if (o instanceof Win32ShellFolder2
1073: && o1 instanceof Win32ShellFolder2) {
1074: // delegates comparison to native method
1075: return compareIDsByColumn(getIShellFolder(),
1076: ((Win32ShellFolder2) o).getRelativePIDL(),
1077: ((Win32ShellFolder2) o1).getRelativePIDL(),
1078: columnIdx);
1079: }
1080: return 0;
1081: }
1082: }
1083:
1084: }
|