Home » openjdk-7 » sun » awt » shell » [javadoc | source]

    1   /*
    2    * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package sun.awt.shell;
   27   
   28   import java.awt.Image;
   29   import java.awt.Toolkit;
   30   import java.awt.image.BufferedImage;
   31   import java.io.File;
   32   import java.io.IOException;
   33   import java.util;
   34   import java.util.concurrent;
   35   import javax.swing.SwingConstants;
   36   
   37   // NOTE: This class supersedes Win32ShellFolder, which was removed from
   38   //       distribution after version 1.4.2.
   39   
   40   /**
   41    * Win32 Shell Folders
   42    * <P>
   43    * <BR>
   44    * There are two fundamental types of shell folders : file system folders
   45    * and non-file system folders.  File system folders are relatively easy
   46    * to deal with.  Non-file system folders are items such as My Computer,
   47    * Network Neighborhood, and the desktop.  Some of these non-file system
   48    * folders have special values and properties.
   49    * <P>
   50    * <BR>
   51    * Win32 keeps two basic data structures for shell folders.  The first
   52    * of these is called an ITEMIDLIST.  Usually a pointer, called an
   53    * LPITEMIDLIST, or more frequently just "PIDL".  This structure holds
   54    * a series of identifiers and can be either relative to the desktop
   55    * (an absolute PIDL), or relative to the shell folder that contains them.
   56    * Some Win32 functions can take absolute or relative PIDL values, and
   57    * others can only accept relative values.
   58    * <BR>
   59    * The second data structure is an IShellFolder COM interface.  Using
   60    * this interface, one can enumerate the relative PIDLs in a shell
   61    * folder, get attributes, etc.
   62    * <BR>
   63    * All Win32ShellFolder2 objects which are folder types (even non-file
   64    * system folders) contain an IShellFolder object. Files are named in
   65    * directories via relative PIDLs.
   66    *
   67    * @author Michael Martak
   68    * @author Leif Samuelsson
   69    * @author Kenneth Russell
   70    * @since 1.4 */
   71   
   72   final class Win32ShellFolder2 extends ShellFolder {
   73   
   74       private static native void initIDs();
   75   
   76       static {
   77           initIDs();
   78       }
   79   
   80       // Win32 Shell Folder Constants
   81       public static final int DESKTOP = 0x0000;
   82       public static final int INTERNET = 0x0001;
   83       public static final int PROGRAMS = 0x0002;
   84       public static final int CONTROLS = 0x0003;
   85       public static final int PRINTERS = 0x0004;
   86       public static final int PERSONAL = 0x0005;
   87       public static final int FAVORITES = 0x0006;
   88       public static final int STARTUP = 0x0007;
   89       public static final int RECENT = 0x0008;
   90       public static final int SENDTO = 0x0009;
   91       public static final int BITBUCKET = 0x000a;
   92       public static final int STARTMENU = 0x000b;
   93       public static final int DESKTOPDIRECTORY = 0x0010;
   94       public static final int DRIVES = 0x0011;
   95       public static final int NETWORK = 0x0012;
   96       public static final int NETHOOD = 0x0013;
   97       public static final int FONTS = 0x0014;
   98       public static final int TEMPLATES = 0x0015;
   99       public static final int COMMON_STARTMENU = 0x0016;
  100       public static final int COMMON_PROGRAMS = 0X0017;
  101       public static final int COMMON_STARTUP = 0x0018;
  102       public static final int COMMON_DESKTOPDIRECTORY = 0x0019;
  103       public static final int APPDATA = 0x001a;
  104       public static final int PRINTHOOD = 0x001b;
  105       public static final int ALTSTARTUP = 0x001d;
  106       public static final int COMMON_ALTSTARTUP = 0x001e;
  107       public static final int COMMON_FAVORITES = 0x001f;
  108       public static final int INTERNET_CACHE = 0x0020;
  109       public static final int COOKIES = 0x0021;
  110       public static final int HISTORY = 0x0022;
  111   
  112       // Win32 shell folder attributes
  113       public static final int ATTRIB_CANCOPY          = 0x00000001;
  114       public static final int ATTRIB_CANMOVE          = 0x00000002;
  115       public static final int ATTRIB_CANLINK          = 0x00000004;
  116       public static final int ATTRIB_CANRENAME        = 0x00000010;
  117       public static final int ATTRIB_CANDELETE        = 0x00000020;
  118       public static final int ATTRIB_HASPROPSHEET     = 0x00000040;
  119       public static final int ATTRIB_DROPTARGET       = 0x00000100;
  120       public static final int ATTRIB_LINK             = 0x00010000;
  121       public static final int ATTRIB_SHARE            = 0x00020000;
  122       public static final int ATTRIB_READONLY         = 0x00040000;
  123       public static final int ATTRIB_GHOSTED          = 0x00080000;
  124       public static final int ATTRIB_HIDDEN           = 0x00080000;
  125       public static final int ATTRIB_FILESYSANCESTOR  = 0x10000000;
  126       public static final int ATTRIB_FOLDER           = 0x20000000;
  127       public static final int ATTRIB_FILESYSTEM       = 0x40000000;
  128       public static final int ATTRIB_HASSUBFOLDER     = 0x80000000;
  129       public static final int ATTRIB_VALIDATE         = 0x01000000;
  130       public static final int ATTRIB_REMOVABLE        = 0x02000000;
  131       public static final int ATTRIB_COMPRESSED       = 0x04000000;
  132       public static final int ATTRIB_BROWSABLE        = 0x08000000;
  133       public static final int ATTRIB_NONENUMERATED    = 0x00100000;
  134       public static final int ATTRIB_NEWCONTENT       = 0x00200000;
  135   
  136       // IShellFolder::GetDisplayNameOf constants
  137       public static final int SHGDN_NORMAL            = 0;
  138       public static final int SHGDN_INFOLDER          = 1;
  139       public static final int SHGDN_INCLUDE_NONFILESYS= 0x2000;
  140       public static final int SHGDN_FORADDRESSBAR     = 0x4000;
  141       public static final int SHGDN_FORPARSING        = 0x8000;
  142   
  143       // Values for system call LoadIcon()
  144       public enum SystemIcon {
  145           IDI_APPLICATION(32512),
  146           IDI_HAND(32513),
  147           IDI_ERROR(32513),
  148           IDI_QUESTION(32514),
  149           IDI_EXCLAMATION(32515),
  150           IDI_WARNING(32515),
  151           IDI_ASTERISK(32516),
  152           IDI_INFORMATION(32516),
  153           IDI_WINLOGO(32517);
  154   
  155           private final int iconID;
  156   
  157           SystemIcon(int iconID) {
  158               this.iconID = iconID;
  159           }
  160   
  161           public int getIconID() {
  162               return iconID;
  163           }
  164       }
  165   
  166       static class FolderDisposer implements sun.java2d.DisposerRecord {
  167           /*
  168            * This is cached as a concession to getFolderType(), which needs
  169            * an absolute PIDL.
  170            */
  171           long absolutePIDL;
  172           /*
  173            * We keep track of shell folders through the IShellFolder
  174            * interface of their parents plus their relative PIDL.
  175            */
  176           long pIShellFolder;
  177           long relativePIDL;
  178   
  179           boolean disposed;
  180           public void dispose() {
  181               if (disposed) return;
  182               invoke(new Callable<Void>() {
  183                   public Void call() {
  184                       if (relativePIDL != 0) {
  185                           releasePIDL(relativePIDL);
  186                       }
  187                       if (absolutePIDL != 0) {
  188                           releasePIDL(absolutePIDL);
  189                       }
  190                       if (pIShellFolder != 0) {
  191                           releaseIShellFolder(pIShellFolder);
  192                       }
  193                       return null;
  194                   }
  195               });
  196               disposed = true;
  197           }
  198       }
  199       FolderDisposer disposer = new FolderDisposer();
  200       private void setIShellFolder(long pIShellFolder) {
  201           disposer.pIShellFolder = pIShellFolder;
  202       }
  203       private void setRelativePIDL(long relativePIDL) {
  204           disposer.relativePIDL = relativePIDL;
  205       }
  206       /*
  207        * The following are for caching various shell folder properties.
  208        */
  209       private long pIShellIcon = -1L;
  210       private String folderType = null;
  211       private String displayName = null;
  212       private Image smallIcon = null;
  213       private Image largeIcon = null;
  214       private Boolean isDir = null;
  215   
  216       /*
  217        * The following is to identify the My Documents folder as being special
  218        */
  219       private boolean isPersonal;
  220   
  221       private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {
  222           String path = getFileSystemPath(csidl);
  223           return path == null
  224                   ? ("ShellFolder: 0x" + Integer.toHexString(csidl))
  225                   : path;
  226       }
  227   
  228       /**
  229        * Create a system special shell folder, such as the
  230        * desktop or Network Neighborhood.
  231        */
  232       Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {
  233           // Desktop is parent of DRIVES and NETWORK, not necessarily
  234           // other special shell folders.
  235           super(null, composePathForCsidl(csidl));
  236   
  237           invoke(new Callable<Void>() {
  238               public Void call() throws InterruptedException {
  239                   if (csidl == DESKTOP) {
  240                       initDesktop();
  241                   } else {
  242                       initSpecial(getDesktop().getIShellFolder(), csidl);
  243                       // At this point, the native method initSpecial() has set our relativePIDL
  244                       // relative to the Desktop, which may not be our immediate parent. We need
  245                       // to traverse this ID list and break it into a chain of shell folders from
  246                       // the top, with each one having an immediate parent and a relativePIDL
  247                       // relative to that parent.
  248                       long pIDL = disposer.relativePIDL;
  249                       parent = getDesktop();
  250                       while (pIDL != 0) {
  251                           // Get a child pidl relative to 'parent'
  252                           long childPIDL = copyFirstPIDLEntry(pIDL);
  253                           if (childPIDL != 0) {
  254                               // Get a handle to the the rest of the ID list
  255                               // i,e, parent's grandchilren and down
  256                               pIDL = getNextPIDLEntry(pIDL);
  257                               if (pIDL != 0) {
  258                                   // Now we know that parent isn't immediate to 'this' because it
  259                                   // has a continued ID list. Create a shell folder for this child
  260                                   // pidl and make it the new 'parent'.
  261                                   parent = new Win32ShellFolder2((Win32ShellFolder2) parent, childPIDL);
  262                               } else {
  263                                   // No grandchildren means we have arrived at the parent of 'this',
  264                                   // and childPIDL is directly relative to parent.
  265                                   disposer.relativePIDL = childPIDL;
  266                               }
  267                           } else {
  268                               break;
  269                           }
  270                       }
  271                   }
  272                   return null;
  273               }
  274           }, InterruptedException.class);
  275   
  276           sun.java2d.Disposer.addRecord(this, disposer);
  277       }
  278   
  279   
  280       /**
  281        * Create a system shell folder
  282        */
  283       Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path) {
  284           super(parent, (path != null) ? path : "ShellFolder: ");
  285           this.disposer.pIShellFolder = pIShellFolder;
  286           this.disposer.relativePIDL = relativePIDL;
  287           sun.java2d.Disposer.addRecord(this, disposer);
  288       }
  289   
  290   
  291       /**
  292        * Creates a shell folder with a parent and relative PIDL
  293        */
  294       Win32ShellFolder2(final Win32ShellFolder2 parent, final long relativePIDL) throws InterruptedException {
  295           super(parent,
  296               invoke(new Callable<String>() {
  297                   public String call() {
  298                       return getFileSystemPath(parent.getIShellFolder(), relativePIDL);
  299                   }
  300               }, RuntimeException.class)
  301           );
  302           this.disposer.relativePIDL = relativePIDL;
  303           sun.java2d.Disposer.addRecord(this, disposer);
  304       }
  305   
  306       // Initializes the desktop shell folder
  307       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  308       private native void initDesktop();
  309   
  310       // Initializes a special, non-file system shell folder
  311       // from one of the above constants
  312       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  313       private native void initSpecial(long desktopIShellFolder, int csidl);
  314   
  315       /** Marks this folder as being the My Documents (Personal) folder */
  316       public void setIsPersonal() {
  317           isPersonal = true;
  318       }
  319   
  320       /**
  321        * This method is implemented to make sure that no instances
  322        * of <code>ShellFolder</code> are ever serialized. If <code>isFileSystem()</code> returns
  323        * <code>true</code>, then the object is representable with an instance of
  324        * <code>java.io.File</code> instead. If not, then the object depends
  325        * on native PIDL state and should not be serialized.
  326        *
  327        * @return a <code>java.io.File</code> replacement object. If the folder
  328        * is a not a normal directory, then returns the first non-removable
  329        * drive (normally "C:\").
  330        */
  331       protected Object writeReplace() throws java.io.ObjectStreamException {
  332           return invoke(new Callable<File>() {
  333               public File call() {
  334                   if (isFileSystem()) {
  335                       return new File(getPath());
  336                   } else {
  337                       Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
  338                       if (drives != null) {
  339                           File[] driveRoots = drives.listFiles();
  340                           if (driveRoots != null) {
  341                               for (int i = 0; i < driveRoots.length; i++) {
  342                                   if (driveRoots[i] instanceof Win32ShellFolder2) {
  343                                       Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];
  344                                       if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
  345                                           return new File(sf.getPath());
  346                                       }
  347                                   }
  348                               }
  349                           }
  350                       }
  351                       // Ouch, we have no hard drives. Return something "valid" anyway.
  352                       return new File("C:\\");
  353                   }
  354               }
  355           });
  356       }
  357   
  358   
  359       /**
  360        * Finalizer to clean up any COM objects or PIDLs used by this object.
  361        */
  362       protected void dispose() {
  363           disposer.dispose();
  364       }
  365   
  366   
  367       // Given a (possibly multi-level) relative PIDL (with respect to
  368       // the desktop, at least in all of the usage cases in this code),
  369       // return a pointer to the next entry. Does not mutate the PIDL in
  370       // any way. Returns 0 if the null terminator is reached.
  371       // Needs to be accessible to Win32ShellFolderManager2
  372       static native long getNextPIDLEntry(long pIDL);
  373   
  374       // Given a (possibly multi-level) relative PIDL (with respect to
  375       // the desktop, at least in all of the usage cases in this code),
  376       // copy the first entry into a newly-allocated PIDL. Returns 0 if
  377       // the PIDL is at the end of the list.
  378       // Needs to be accessible to Win32ShellFolderManager2
  379       static native long copyFirstPIDLEntry(long pIDL);
  380   
  381       // Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL
  382       private static native long combinePIDLs(long ppIDL, long pIDL);
  383   
  384       // Release a PIDL object
  385       // Needs to be accessible to Win32ShellFolderManager2
  386       static native void releasePIDL(long pIDL);
  387   
  388       // Release an IShellFolder object
  389       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  390       private static native void releaseIShellFolder(long pIShellFolder);
  391   
  392       /**
  393        * Accessor for IShellFolder
  394        */
  395       private long getIShellFolder() {
  396           if (disposer.pIShellFolder == 0) {
  397               try {
  398                   disposer.pIShellFolder = invoke(new Callable<Long>() {
  399                       public Long call() {
  400                           assert(isDirectory());
  401                           assert(parent != null);
  402                           long parentIShellFolder = getParentIShellFolder();
  403                           if (parentIShellFolder == 0) {
  404                               throw new InternalError("Parent IShellFolder was null for "
  405                                       + getAbsolutePath());
  406                           }
  407                           // We are a directory with a parent and a relative PIDL.
  408                           // We want to bind to the parent so we get an
  409                           // IShellFolder instance associated with us.
  410                           long pIShellFolder = bindToObject(parentIShellFolder,
  411                                   disposer.relativePIDL);
  412                           if (pIShellFolder == 0) {
  413                               throw new InternalError("Unable to bind "
  414                                       + getAbsolutePath() + " to parent");
  415                           }
  416                           return pIShellFolder;
  417                       }
  418                   }, RuntimeException.class);
  419               } catch (InterruptedException e) {
  420                   // Ignore error
  421               }
  422           }
  423           return disposer.pIShellFolder;
  424       }
  425   
  426       /**
  427        * Get the parent ShellFolder's IShellFolder interface
  428        */
  429       public long getParentIShellFolder() {
  430           Win32ShellFolder2 parent = (Win32ShellFolder2)getParentFile();
  431           if (parent == null) {
  432               // Parent should only be null if this is the desktop, whose
  433               // relativePIDL is relative to its own IShellFolder.
  434               return getIShellFolder();
  435           }
  436           return parent.getIShellFolder();
  437       }
  438   
  439       /**
  440        * Accessor for relative PIDL
  441        */
  442       public long getRelativePIDL() {
  443           if (disposer.relativePIDL == 0) {
  444               throw new InternalError("Should always have a relative PIDL");
  445           }
  446           return disposer.relativePIDL;
  447       }
  448   
  449       private long getAbsolutePIDL() {
  450           if (parent == null) {
  451               // This is the desktop
  452               return getRelativePIDL();
  453           } else {
  454               if (disposer.absolutePIDL == 0) {
  455                   disposer.absolutePIDL = combinePIDLs(((Win32ShellFolder2)parent).getAbsolutePIDL(), getRelativePIDL());
  456               }
  457   
  458               return disposer.absolutePIDL;
  459           }
  460       }
  461   
  462       /**
  463        * Helper function to return the desktop
  464        */
  465       public Win32ShellFolder2 getDesktop() {
  466           return Win32ShellFolderManager2.getDesktop();
  467       }
  468   
  469       /**
  470        * Helper function to return the desktop IShellFolder interface
  471        */
  472       public long getDesktopIShellFolder() {
  473           return getDesktop().getIShellFolder();
  474       }
  475   
  476       private static boolean pathsEqual(String path1, String path2) {
  477           // Same effective implementation as Win32FileSystem
  478           return path1.equalsIgnoreCase(path2);
  479       }
  480   
  481       /**
  482        * Check to see if two ShellFolder objects are the same
  483        */
  484       public boolean equals(Object o) {
  485           if (o == null || !(o instanceof Win32ShellFolder2)) {
  486               // Short-circuit circuitous delegation path
  487               if (!(o instanceof File)) {
  488                   return super.equals(o);
  489               }
  490               return pathsEqual(getPath(), ((File) o).getPath());
  491           }
  492           Win32ShellFolder2 rhs = (Win32ShellFolder2) o;
  493           if ((parent == null && rhs.parent != null) ||
  494               (parent != null && rhs.parent == null)) {
  495               return false;
  496           }
  497   
  498           if (isFileSystem() && rhs.isFileSystem()) {
  499               // Only folders with identical parents can be equal
  500               return (pathsEqual(getPath(), rhs.getPath()) &&
  501                       (parent == rhs.parent || parent.equals(rhs.parent)));
  502           }
  503   
  504           if (parent == rhs.parent || parent.equals(rhs.parent)) {
  505               try {
  506                   return pidlsEqual(getParentIShellFolder(), disposer.relativePIDL, rhs.disposer.relativePIDL);
  507               } catch (InterruptedException e) {
  508                   return false;
  509               }
  510           }
  511   
  512           return false;
  513       }
  514   
  515       private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2)
  516               throws InterruptedException {
  517           return invoke(new Callable<Boolean>() {
  518               public Boolean call() {
  519                   return compareIDs(pIShellFolder, pidl1, pidl2) == 0;
  520               }
  521           }, RuntimeException.class);
  522       }
  523   
  524       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  525       private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);
  526   
  527       private volatile Boolean cachedIsFileSystem;
  528   
  529       /**
  530        * @return Whether this is a file system shell folder
  531        */
  532       public boolean isFileSystem() {
  533           if (cachedIsFileSystem == null) {
  534               cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);
  535           }
  536   
  537           return cachedIsFileSystem;
  538       }
  539   
  540       /**
  541        * Return whether the given attribute flag is set for this object
  542        */
  543       public boolean hasAttribute(final int attribute) {
  544           Boolean result = invoke(new Callable<Boolean>() {
  545               public Boolean call() {
  546                   // Caching at this point doesn't seem to be cost efficient
  547                   return (getAttributes0(getParentIShellFolder(),
  548                       getRelativePIDL(), attribute)
  549                       & attribute) != 0;
  550               }
  551           });
  552   
  553           return result != null && result;
  554       }
  555   
  556       /**
  557        * Returns the queried attributes specified in attrsMask.
  558        *
  559        * Could plausibly be used for attribute caching but have to be
  560        * very careful not to touch network drives and file system roots
  561        * with a full attrsMask
  562        * NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  563        */
  564   
  565       private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
  566   
  567       // Return the path to the underlying file system object
  568       // Should be called from the COM thread
  569       private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
  570           int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
  571           if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
  572                   getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
  573   
  574               String s =
  575                       getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
  576                               getLinkLocation(parentIShellFolder, relativePIDL, false));
  577               if (s != null && s.startsWith("\\\\")) {
  578                   return s;
  579               }
  580           }
  581           return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_FORPARSING);
  582       }
  583   
  584       // Needs to be accessible to Win32ShellFolderManager2
  585       static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {
  586           return invoke(new Callable<String>() {
  587               public String call() throws IOException {
  588                   return getFileSystemPath0(csidl);
  589               }
  590           }, IOException.class);
  591       }
  592   
  593       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  594       private static native String getFileSystemPath0(int csidl) throws IOException;
  595   
  596       // Return whether the path is a network root.
  597       // Path is assumed to be non-null
  598       private static boolean isNetworkRoot(String path) {
  599           return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));
  600       }
  601   
  602       /**
  603        * @return The parent shell folder of this shell folder, null if
  604        * there is no parent
  605        */
  606       public File getParentFile() {
  607           return parent;
  608       }
  609   
  610       public boolean isDirectory() {
  611           if (isDir == null) {
  612               // Folders with SFGAO_BROWSABLE have "shell extension" handlers and are
  613               // not traversable in JFileChooser.
  614               if (hasAttribute(ATTRIB_FOLDER) && !hasAttribute(ATTRIB_BROWSABLE)) {
  615                   isDir = Boolean.TRUE;
  616               } else if (isLink()) {
  617                   ShellFolder linkLocation = getLinkLocation(false);
  618                   isDir = Boolean.valueOf(linkLocation != null && linkLocation.isDirectory());
  619               } else {
  620                   isDir = Boolean.FALSE;
  621               }
  622           }
  623           return isDir.booleanValue();
  624       }
  625   
  626       /*
  627        * Functions for enumerating an IShellFolder's children
  628        */
  629       // Returns an IEnumIDList interface for an IShellFolder.  The value
  630       // returned must be released using releaseEnumObjects().
  631       private long getEnumObjects(final boolean includeHiddenFiles) throws InterruptedException {
  632           return invoke(new Callable<Long>() {
  633               public Long call() {
  634                   boolean isDesktop = disposer.pIShellFolder == getDesktopIShellFolder();
  635   
  636                   return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);
  637               }
  638           }, RuntimeException.class);
  639       }
  640   
  641       // Returns an IEnumIDList interface for an IShellFolder.  The value
  642       // returned must be released using releaseEnumObjects().
  643       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  644       private native long getEnumObjects(long pIShellFolder, boolean isDesktop,
  645                                          boolean includeHiddenFiles);
  646       // Returns the next sequential child as a relative PIDL
  647       // from an IEnumIDList interface.  The value returned must
  648       // be released using releasePIDL().
  649       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  650       private native long getNextChild(long pEnumObjects);
  651       // Releases the IEnumIDList interface
  652       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  653       private native void releaseEnumObjects(long pEnumObjects);
  654   
  655       // Returns the IShellFolder of a child from a parent IShellFolder
  656       // and a relative PIDL.  The value returned must be released
  657       // using releaseIShellFolder().
  658       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  659       private static native long bindToObject(long parentIShellFolder, long pIDL);
  660   
  661       /**
  662        * @return An array of shell folders that are children of this shell folder
  663        *         object. The array will be empty if the folder is empty.  Returns
  664        *         <code>null</code> if this shellfolder does not denote a directory.
  665        */
  666       public File[] listFiles(final boolean includeHiddenFiles) {
  667           SecurityManager security = System.getSecurityManager();
  668           if (security != null) {
  669               security.checkRead(getPath());
  670           }
  671   
  672           try {
  673               return invoke(new Callable<File[]>() {
  674                   public File[] call() throws InterruptedException {
  675                       if (!isDirectory()) {
  676                           return null;
  677                       }
  678                       // Links to directories are not directories and cannot be parents.
  679                       // This does not apply to folders in My Network Places (NetHood)
  680                       // because they are both links and real directories!
  681                       if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
  682                           return new File[0];
  683                       }
  684   
  685                       Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();
  686                       Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();
  687   
  688                       // If we are a directory, we have a parent and (at least) a
  689                       // relative PIDL. We must first ensure we are bound to the
  690                       // parent so we have an IShellFolder to query.
  691                       long pIShellFolder = getIShellFolder();
  692                       // Now we can enumerate the objects in this folder.
  693                       ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
  694                       long pEnumObjects = getEnumObjects(includeHiddenFiles);
  695                       if (pEnumObjects != 0) {
  696                           try {
  697                               long childPIDL;
  698                               int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
  699                               do {
  700                                   childPIDL = getNextChild(pEnumObjects);
  701                                   boolean releasePIDL = true;
  702                                   if (childPIDL != 0 &&
  703                                           (getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
  704                                       Win32ShellFolder2 childFolder;
  705                                       if (Win32ShellFolder2.this.equals(desktop)
  706                                               && personal != null
  707                                               && pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
  708                                           childFolder = personal;
  709                                       } else {
  710                                           childFolder = new Win32ShellFolder2(Win32ShellFolder2.this, childPIDL);
  711                                           releasePIDL = false;
  712                                       }
  713                                       list.add(childFolder);
  714                                   }
  715                                   if (releasePIDL) {
  716                                       releasePIDL(childPIDL);
  717                                   }
  718                               } while (childPIDL != 0 && !Thread.currentThread().isInterrupted());
  719                           } finally {
  720                               releaseEnumObjects(pEnumObjects);
  721                           }
  722                       }
  723                       return Thread.currentThread().isInterrupted()
  724                           ? new File[0]
  725                           : list.toArray(new ShellFolder[list.size()]);
  726                   }
  727               }, InterruptedException.class);
  728           } catch (InterruptedException e) {
  729               return new File[0];
  730           }
  731       }
  732   
  733   
  734       /**
  735        * Look for (possibly special) child folder by it's path
  736        *
  737        * @return The child shellfolder, or null if not found.
  738        */
  739       Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {
  740           return invoke(new Callable<Win32ShellFolder2>() {
  741               public Win32ShellFolder2 call() throws InterruptedException {
  742                   long pIShellFolder = getIShellFolder();
  743                   long pEnumObjects = getEnumObjects(true);
  744                   Win32ShellFolder2 child = null;
  745                   long childPIDL;
  746   
  747                   while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
  748                       if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
  749                           String path = getFileSystemPath(pIShellFolder, childPIDL);
  750                           if (path != null && path.equalsIgnoreCase(filePath)) {
  751                               long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
  752                               child = new Win32ShellFolder2(Win32ShellFolder2.this,
  753                                       childIShellFolder, childPIDL, path);
  754                               break;
  755                           }
  756                       }
  757                       releasePIDL(childPIDL);
  758                   }
  759                   releaseEnumObjects(pEnumObjects);
  760                   return child;
  761               }
  762           }, InterruptedException.class);
  763       }
  764   
  765       private volatile Boolean cachedIsLink;
  766   
  767       /**
  768        * @return Whether this shell folder is a link
  769        */
  770       public boolean isLink() {
  771           if (cachedIsLink == null) {
  772               cachedIsLink = hasAttribute(ATTRIB_LINK);
  773           }
  774   
  775           return cachedIsLink;
  776       }
  777   
  778       /**
  779        * @return Whether this shell folder is marked as hidden
  780        */
  781       public boolean isHidden() {
  782           return hasAttribute(ATTRIB_HIDDEN);
  783       }
  784   
  785   
  786       // Return the link location of a shell folder
  787       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  788       private static native long getLinkLocation(long parentIShellFolder,
  789                                           long relativePIDL, boolean resolve);
  790   
  791       /**
  792        * @return The shell folder linked to by this shell folder, or null
  793        * if this shell folder is not a link or is a broken or invalid link
  794        */
  795       public ShellFolder getLinkLocation()  {
  796           return getLinkLocation(true);
  797       }
  798   
  799       private ShellFolder getLinkLocation(final boolean resolve) {
  800           return invoke(new Callable<ShellFolder>() {
  801               public ShellFolder call() {
  802                   if (!isLink()) {
  803                       return null;
  804                   }
  805   
  806                   ShellFolder location = null;
  807                   long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
  808                           getRelativePIDL(), resolve);
  809                   if (linkLocationPIDL != 0) {
  810                       try {
  811                           location =
  812                                   Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
  813                                           linkLocationPIDL);
  814                       } catch (InterruptedException e) {
  815                           // Return null
  816                       } catch (InternalError e) {
  817                           // Could be a link to a non-bindable object, such as a network connection
  818                           // TODO: getIShellFolder() should throw FileNotFoundException instead
  819                       }
  820                   }
  821                   return location;
  822               }
  823           });
  824       }
  825   
  826       // Parse a display name into a PIDL relative to the current IShellFolder.
  827       long parseDisplayName(final String name) throws IOException, InterruptedException {
  828           return invoke(new Callable<Long>() {
  829               public Long call() throws IOException {
  830                   return parseDisplayName0(getIShellFolder(), name);
  831               }
  832           }, IOException.class);
  833       }
  834   
  835       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  836       private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;
  837   
  838       // Return the display name of a shell folder
  839       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  840       private static native String getDisplayNameOf(long parentIShellFolder,
  841                                                     long relativePIDL,
  842                                                     int attrs);
  843   
  844       /**
  845        * @return The name used to display this shell folder
  846        */
  847       public String getDisplayName() {
  848           if (displayName == null) {
  849               displayName =
  850                   invoke(new Callable<String>() {
  851                       public String call() {
  852                           return getDisplayNameOf(getParentIShellFolder(),
  853                                   getRelativePIDL(), SHGDN_NORMAL);
  854                       }
  855                   });
  856           }
  857           return displayName;
  858       }
  859   
  860       // Return the folder type of a shell folder
  861       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  862       private static native String getFolderType(long pIDL);
  863   
  864       /**
  865        * @return The type of shell folder as a string
  866        */
  867       public String getFolderType() {
  868           if (folderType == null) {
  869               final long absolutePIDL = getAbsolutePIDL();
  870               folderType =
  871                   invoke(new Callable<String>() {
  872                       public String call() {
  873                           return getFolderType(absolutePIDL);
  874                       }
  875                   });
  876           }
  877           return folderType;
  878       }
  879   
  880       // Return the executable type of a file system shell folder
  881       private native String getExecutableType(String path);
  882   
  883       /**
  884        * @return The executable type as a string
  885        */
  886       public String getExecutableType() {
  887           if (!isFileSystem()) {
  888               return null;
  889           }
  890           return getExecutableType(getAbsolutePath());
  891       }
  892   
  893   
  894   
  895       // Icons
  896   
  897       private static Map smallSystemImages = new HashMap();
  898       private static Map largeSystemImages = new HashMap();
  899       private static Map smallLinkedSystemImages = new HashMap();
  900       private static Map largeLinkedSystemImages = new HashMap();
  901   
  902       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  903       private static native long getIShellIcon(long pIShellFolder);
  904   
  905       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  906       private static native int getIconIndex(long parentIShellIcon, long relativePIDL);
  907   
  908       // Return the icon of a file system shell folder in the form of an HICON
  909       private static native long getIcon(String absolutePath, boolean getLargeIcon);
  910   
  911       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  912       private static native long extractIcon(long parentIShellFolder, long relativePIDL,
  913                                              boolean getLargeIcon);
  914   
  915       // Returns an icon from the Windows system icon list in the form of an HICON
  916       private static native long getSystemIcon(int iconID);
  917       private static native long getIconResource(String libName, int iconID,
  918                                                  int cxDesired, int cyDesired,
  919                                                  boolean useVGAColors);
  920                                                  // Note: useVGAColors is ignored on XP and later
  921   
  922       // Return the bits from an HICON.  This has a side effect of setting
  923       // the imageHash variable for efficient caching / comparing.
  924       private static native int[] getIconBits(long hIcon, int iconSize);
  925       // Dispose the HICON
  926       private static native void disposeIcon(long hIcon);
  927   
  928       static native int[] getStandardViewButton0(int iconIndex);
  929   
  930       // Should be called from the COM thread
  931       private long getIShellIcon() {
  932           if (pIShellIcon == -1L) {
  933               pIShellIcon = getIShellIcon(getIShellFolder());
  934           }
  935   
  936           return pIShellIcon;
  937       }
  938   
  939       private static Image makeIcon(long hIcon, boolean getLargeIcon) {
  940           if (hIcon != 0L && hIcon != -1L) {
  941               // Get the bits.  This has the side effect of setting the imageHash value for this object.
  942               int size = getLargeIcon ? 32 : 16;
  943               int[] iconBits = getIconBits(hIcon, size);
  944               if (iconBits != null) {
  945                   BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
  946                   img.setRGB(0, 0, size, size, iconBits, 0, size);
  947                   return img;
  948               }
  949           }
  950           return null;
  951       }
  952   
  953   
  954       /**
  955        * @return The icon image used to display this shell folder
  956        */
  957       public Image getIcon(final boolean getLargeIcon) {
  958           Image icon = getLargeIcon ? largeIcon : smallIcon;
  959           if (icon == null) {
  960               icon =
  961                   invoke(new Callable<Image>() {
  962                       public Image call() {
  963                           Image newIcon = null;
  964                           if (isFileSystem()) {
  965                               long parentIShellIcon = (parent != null)
  966                                   ? ((Win32ShellFolder2) parent).getIShellIcon()
  967                                   : 0L;
  968                               long relativePIDL = getRelativePIDL();
  969   
  970                               // These are cached per type (using the index in the system image list)
  971                               int index = getIconIndex(parentIShellIcon, relativePIDL);
  972                               if (index > 0) {
  973                                   Map imageCache;
  974                                   if (isLink()) {
  975                                       imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
  976                                   } else {
  977                                       imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
  978                                   }
  979                                   newIcon = (Image) imageCache.get(Integer.valueOf(index));
  980                                   if (newIcon == null) {
  981                                       long hIcon = getIcon(getAbsolutePath(), getLargeIcon);
  982                                       newIcon = makeIcon(hIcon, getLargeIcon);
  983                                       disposeIcon(hIcon);
  984                                       if (newIcon != null) {
  985                                           imageCache.put(Integer.valueOf(index), newIcon);
  986                                       }
  987                                   }
  988                               }
  989                           }
  990   
  991                           if (newIcon == null) {
  992                               // These are only cached per object
  993                               long hIcon = extractIcon(getParentIShellFolder(),
  994                                   getRelativePIDL(), getLargeIcon);
  995                               newIcon = makeIcon(hIcon, getLargeIcon);
  996                               disposeIcon(hIcon);
  997                           }
  998   
  999                           if (newIcon == null) {
 1000                               newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);
 1001                           }
 1002                           return newIcon;
 1003                       }
 1004                   });
 1005               if (getLargeIcon) {
 1006                   largeIcon = icon;
 1007               } else {
 1008                   smallIcon = icon;
 1009               }
 1010           }
 1011           return icon;
 1012       }
 1013   
 1014       /**
 1015        * Gets an icon from the Windows system icon list as an <code>Image</code>
 1016        */
 1017       static Image getSystemIcon(SystemIcon iconType) {
 1018           long hIcon = getSystemIcon(iconType.getIconID());
 1019           Image icon = makeIcon(hIcon, true);
 1020           disposeIcon(hIcon);
 1021           return icon;
 1022       }
 1023   
 1024       /**
 1025        * Gets an icon from the Windows system icon list as an <code>Image</code>
 1026        */
 1027       static Image getShell32Icon(int iconID, boolean getLargeIcon) {
 1028           boolean useVGAColors = true; // Will be ignored on XP and later
 1029   
 1030           int size = getLargeIcon ? 32 : 16;
 1031   
 1032           Toolkit toolkit = Toolkit.getDefaultToolkit();
 1033           String shellIconBPP = (String)toolkit.getDesktopProperty("win.icon.shellIconBPP");
 1034           if (shellIconBPP != null) {
 1035               useVGAColors = shellIconBPP.equals("4");
 1036           }
 1037   
 1038           long hIcon = getIconResource("shell32.dll", iconID, size, size, useVGAColors);
 1039           if (hIcon != 0) {
 1040               Image icon = makeIcon(hIcon, getLargeIcon);
 1041               disposeIcon(hIcon);
 1042               return icon;
 1043           }
 1044           return null;
 1045       }
 1046   
 1047       /**
 1048        * Returns the canonical form of this abstract pathname.  Equivalent to
 1049        * <code>new&nbsp;Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())</code>.
 1050        *
 1051        * @see java.io.File#getCanonicalFile
 1052        */
 1053       public File getCanonicalFile() throws IOException {
 1054           return this;
 1055       }
 1056   
 1057       /*
 1058        * Indicates whether this is a special folder (includes My Documents)
 1059        */
 1060       public boolean isSpecial() {
 1061           return isPersonal || !isFileSystem() || (this == getDesktop());
 1062       }
 1063   
 1064       /**
 1065        * Compares this object with the specified object for order.
 1066        *
 1067        * @see sun.awt.shell.ShellFolder#compareTo(File)
 1068        */
 1069       public int compareTo(File file2) {
 1070           if (!(file2 instanceof Win32ShellFolder2)) {
 1071               if (isFileSystem() && !isSpecial()) {
 1072                   return super.compareTo(file2);
 1073               } else {
 1074                   return -1; // Non-file shellfolders sort before files
 1075               }
 1076           }
 1077           return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);
 1078       }
 1079   
 1080       // native constants from commctrl.h
 1081       private static final int LVCFMT_LEFT = 0;
 1082       private static final int LVCFMT_RIGHT = 1;
 1083       private static final int LVCFMT_CENTER = 2;
 1084   
 1085       public ShellFolderColumnInfo[] getFolderColumns() {
 1086           return invoke(new Callable<ShellFolderColumnInfo[]>() {
 1087               public ShellFolderColumnInfo[] call() {
 1088                   ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
 1089   
 1090                   if (columns != null) {
 1091                       List<ShellFolderColumnInfo> notNullColumns =
 1092                               new ArrayList<ShellFolderColumnInfo>();
 1093                       for (int i = 0; i < columns.length; i++) {
 1094                           ShellFolderColumnInfo column = columns[i];
 1095                           if (column != null) {
 1096                               column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
 1097                                       ? SwingConstants.RIGHT
 1098                                       : column.getAlignment() == LVCFMT_CENTER
 1099                                       ? SwingConstants.CENTER
 1100                                       : SwingConstants.LEADING);
 1101   
 1102                               column.setComparator(new ColumnComparator(getIShellFolder(), i));
 1103   
 1104                               notNullColumns.add(column);
 1105                           }
 1106                       }
 1107                       columns = new ShellFolderColumnInfo[notNullColumns.size()];
 1108                       notNullColumns.toArray(columns);
 1109                   }
 1110                   return columns;
 1111               }
 1112           });
 1113       }
 1114   
 1115       public Object getFolderColumnValue(final int column) {
 1116           return invoke(new Callable<Object>() {
 1117               public Object call() {
 1118                   return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
 1119               }
 1120           });
 1121       }
 1122   
 1123       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
 1124       private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
 1125   
 1126       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
 1127       private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
 1128   
 1129       // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
 1130       private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
 1131   
 1132   
 1133       public void sortChildren(final List<? extends File> files) {
 1134           // To avoid loads of synchronizations with Invoker and improve performance we
 1135           // synchronize the whole code of the sort method once
 1136           invoke(new Callable<Void>() {
 1137               public Void call() {
 1138                   Collections.sort(files, new ColumnComparator(getIShellFolder(), 0));
 1139   
 1140                   return null;
 1141               }
 1142           });
 1143       }
 1144   
 1145       private static class ColumnComparator implements Comparator<File> {
 1146           private final long parentIShellFolder;
 1147   
 1148           private final int columnIdx;
 1149   
 1150           public ColumnComparator(long parentIShellFolder, int columnIdx) {
 1151               this.parentIShellFolder = parentIShellFolder;
 1152               this.columnIdx = columnIdx;
 1153           }
 1154   
 1155           // compares 2 objects within this folder by the specified column
 1156           public int compare(final File o, final File o1) {
 1157               Integer result = invoke(new Callable<Integer>() {
 1158                   public Integer call() {
 1159                       if (o instanceof Win32ShellFolder2
 1160                           && o1 instanceof Win32ShellFolder2) {
 1161                           // delegates comparison to native method
 1162                           return compareIDsByColumn(parentIShellFolder,
 1163                               ((Win32ShellFolder2) o).getRelativePIDL(),
 1164                               ((Win32ShellFolder2) o1).getRelativePIDL(),
 1165                               columnIdx);
 1166                       }
 1167                       return 0;
 1168                   }
 1169               });
 1170   
 1171               return result == null ? 0 : result;
 1172           }
 1173       }
 1174   }

Home » openjdk-7 » sun » awt » shell » [javadoc | source]