jdk/src/windows/classes/sun/awt/shell/Win32ShellFolder2.java
changeset 2489 5052722686e2
parent 715 f16baef3a20e
child 2817 f171f2417978
equal deleted inserted replaced
2488:4d54c9133cda 2489:5052722686e2
    30 import java.awt.image.BufferedImage;
    30 import java.awt.image.BufferedImage;
    31 import java.io.File;
    31 import java.io.File;
    32 import java.io.FileNotFoundException;
    32 import java.io.FileNotFoundException;
    33 import java.io.IOException;
    33 import java.io.IOException;
    34 import java.util.*;
    34 import java.util.*;
       
    35 import java.util.concurrent.*;
    35 import javax.swing.SwingConstants;
    36 import javax.swing.SwingConstants;
    36 
    37 
    37 // NOTE: This class supersedes Win32ShellFolder, which was removed from
    38 // NOTE: This class supersedes Win32ShellFolder, which was removed from
    38 //       distribution after version 1.4.2.
    39 //       distribution after version 1.4.2.
    39 
    40 
   182         long relativePIDL;
   183         long relativePIDL;
   183 
   184 
   184         boolean disposed;
   185         boolean disposed;
   185         public void dispose() {
   186         public void dispose() {
   186             if (disposed) return;
   187             if (disposed) return;
   187             if (relativePIDL != 0) {
   188             ShellFolder.getInvoker().invoke(new Callable<Void>() {
   188                 releasePIDL(relativePIDL);
   189                 public Void call() throws Exception {
   189             }
   190                     if (relativePIDL != 0) {
   190             if (absolutePIDL != 0) {
   191                         releasePIDL(relativePIDL);
   191                 releasePIDL(absolutePIDL);
   192                     }
   192             }
   193                     if (absolutePIDL != 0) {
   193             if (pIShellFolder != 0) {
   194                         releasePIDL(absolutePIDL);
   194                 releaseIShellFolder(pIShellFolder);
   195                     }
   195             }
   196                     if (pIShellFolder != 0) {
       
   197                         releaseIShellFolder(pIShellFolder);
       
   198                     }
       
   199                     return null;
       
   200                 }
       
   201             });
   196             disposed = true;
   202             disposed = true;
   197         }
   203         }
   198     }
   204     }
   199     FolderDisposer disposer = new FolderDisposer();
   205     FolderDisposer disposer = new FolderDisposer();
   200     private void setIShellFolder(long pIShellFolder) {
   206     private void setIShellFolder(long pIShellFolder) {
   216     /*
   222     /*
   217      * The following is to identify the My Documents folder as being special
   223      * The following is to identify the My Documents folder as being special
   218      */
   224      */
   219     private boolean isPersonal;
   225     private boolean isPersonal;
   220 
   226 
       
   227     private static String composePathForCsidl(int csidl) throws IOException {
       
   228         String path = getFileSystemPath(csidl);
       
   229         return path == null
       
   230                 ? ("ShellFolder: 0x" + Integer.toHexString(csidl))
       
   231                 : path;
       
   232     }
   221 
   233 
   222     /**
   234     /**
   223      * Create a system special shell folder, such as the
   235      * Create a system special shell folder, such as the
   224      * desktop or Network Neighborhood.
   236      * desktop or Network Neighborhood.
   225      */
   237      */
   226     Win32ShellFolder2(int csidl) throws IOException {
   238     Win32ShellFolder2(final int csidl) throws IOException {
   227         // Desktop is parent of DRIVES and NETWORK, not necessarily
   239         // Desktop is parent of DRIVES and NETWORK, not necessarily
   228         // other special shell folders.
   240         // other special shell folders.
   229         super(null,
   241         super(null, composePathForCsidl(csidl));
   230               (getFileSystemPath(csidl) == null)
   242         ShellFolder.getInvoker().invoke(new Callable<Void>() {
   231                 ? ("ShellFolder: 0x"+Integer.toHexString(csidl)) : getFileSystemPath(csidl));
   243             public Void call() throws Exception {
   232         if (csidl == DESKTOP) {
   244                 if (csidl == DESKTOP) {
   233             initDesktop();
   245                     initDesktop();
   234         } else {
       
   235             initSpecial(getDesktop().getIShellFolder(), csidl);
       
   236             // At this point, the native method initSpecial() has set our relativePIDL
       
   237             // relative to the Desktop, which may not be our immediate parent. We need
       
   238             // to traverse this ID list and break it into a chain of shell folders from
       
   239             // the top, with each one having an immediate parent and a relativePIDL
       
   240             // relative to that parent.
       
   241             long pIDL = disposer.relativePIDL;
       
   242             parent = getDesktop();
       
   243             while (pIDL != 0) {
       
   244                 // Get a child pidl relative to 'parent'
       
   245                 long childPIDL = copyFirstPIDLEntry(pIDL);
       
   246                 if (childPIDL != 0) {
       
   247                     // Get a handle to the the rest of the ID list
       
   248                     // i,e, parent's grandchilren and down
       
   249                     pIDL = getNextPIDLEntry(pIDL);
       
   250                     if (pIDL != 0) {
       
   251                         // Now we know that parent isn't immediate to 'this' because it
       
   252                         // has a continued ID list. Create a shell folder for this child
       
   253                         // pidl and make it the new 'parent'.
       
   254                         parent = new Win32ShellFolder2((Win32ShellFolder2)parent, childPIDL);
       
   255                     } else {
       
   256                         // No grandchildren means we have arrived at the parent of 'this',
       
   257                         // and childPIDL is directly relative to parent.
       
   258                         disposer.relativePIDL = childPIDL;
       
   259                     }
       
   260                 } else {
   246                 } else {
   261                     break;
   247                     initSpecial(getDesktop().getIShellFolder(), csidl);
   262                 }
   248                     // At this point, the native method initSpecial() has set our relativePIDL
   263             }
   249                     // relative to the Desktop, which may not be our immediate parent. We need
   264         }
   250                     // to traverse this ID list and break it into a chain of shell folders from
       
   251                     // the top, with each one having an immediate parent and a relativePIDL
       
   252                     // relative to that parent.
       
   253                     long pIDL = disposer.relativePIDL;
       
   254                     parent = getDesktop();
       
   255                     while (pIDL != 0) {
       
   256                         // Get a child pidl relative to 'parent'
       
   257                         long childPIDL = copyFirstPIDLEntry(pIDL);
       
   258                         if (childPIDL != 0) {
       
   259                             // Get a handle to the the rest of the ID list
       
   260                             // i,e, parent's grandchilren and down
       
   261                             pIDL = getNextPIDLEntry(pIDL);
       
   262                             if (pIDL != 0) {
       
   263                                 // Now we know that parent isn't immediate to 'this' because it
       
   264                                 // has a continued ID list. Create a shell folder for this child
       
   265                                 // pidl and make it the new 'parent'.
       
   266                                 parent = new Win32ShellFolder2((Win32ShellFolder2) parent, childPIDL);
       
   267                             } else {
       
   268                                 // No grandchildren means we have arrived at the parent of 'this',
       
   269                                 // and childPIDL is directly relative to parent.
       
   270                                 disposer.relativePIDL = childPIDL;
       
   271                             }
       
   272                         } else {
       
   273                             break;
       
   274                         }
       
   275                     }
       
   276                 }
       
   277                 return null;
       
   278             }
       
   279         });
   265 
   280 
   266         sun.java2d.Disposer.addRecord(this, disposer);
   281         sun.java2d.Disposer.addRecord(this, disposer);
   267     }
   282     }
   268 
   283 
   269 
   284 
   279 
   294 
   280 
   295 
   281     /**
   296     /**
   282      * Creates a shell folder with a parent and relative PIDL
   297      * Creates a shell folder with a parent and relative PIDL
   283      */
   298      */
   284     Win32ShellFolder2(Win32ShellFolder2 parent, long relativePIDL) {
   299     Win32ShellFolder2(final Win32ShellFolder2 parent, final long relativePIDL) {
   285         super(parent, getFileSystemPath(parent.getIShellFolder(), relativePIDL));
   300         super(parent,
       
   301             ShellFolder.getInvoker().invoke(new Callable<String>() {
       
   302                 public String call() throws Exception {
       
   303                     return getFileSystemPath(parent.getIShellFolder(), relativePIDL);
       
   304                 }
       
   305             })
       
   306         );
   286         this.disposer.relativePIDL = relativePIDL;
   307         this.disposer.relativePIDL = relativePIDL;
   287         getAbsolutePath();
   308         getAbsolutePath();
   288         sun.java2d.Disposer.addRecord(this, disposer);
   309         sun.java2d.Disposer.addRecord(this, disposer);
   289     }
   310     }
   290 
   311 
   291     // Initializes the desktop shell folder
   312     // Initializes the desktop shell folder
       
   313     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   292     private native void initDesktop();
   314     private native void initDesktop();
       
   315 
   293     // Initializes a special, non-file system shell folder
   316     // Initializes a special, non-file system shell folder
   294     // from one of the above constants
   317     // from one of the above constants
       
   318     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   295     private native void initSpecial(long desktopIShellFolder, int csidl);
   319     private native void initSpecial(long desktopIShellFolder, int csidl);
   296 
   320 
   297     /** Marks this folder as being the My Documents (Personal) folder */
   321     /** Marks this folder as being the My Documents (Personal) folder */
   298     public void setIsPersonal() {
   322     public void setIsPersonal() {
   299         isPersonal = true;
   323         isPersonal = true;
   309      * @return a <code>java.io.File</code> replacement object. If the folder
   333      * @return a <code>java.io.File</code> replacement object. If the folder
   310      * is a not a normal directory, then returns the first non-removable
   334      * is a not a normal directory, then returns the first non-removable
   311      * drive (normally "C:\").
   335      * drive (normally "C:\").
   312      */
   336      */
   313     protected Object writeReplace() throws java.io.ObjectStreamException {
   337     protected Object writeReplace() throws java.io.ObjectStreamException {
   314         if (isFileSystem()) {
   338         return ShellFolder.getInvoker().invoke(new Callable<File>() {
   315             return new File(getPath());
   339             public File call() throws Exception {
   316         } else {
   340                 if (isFileSystem()) {
   317             Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
   341                     return new File(getPath());
   318             if (drives != null) {
   342                 } else {
   319                 File[] driveRoots = drives.listFiles();
   343                     Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
   320                 if (driveRoots != null) {
   344                     if (drives != null) {
   321                     for (int i = 0; i < driveRoots.length; i++) {
   345                         File[] driveRoots = drives.listFiles();
   322                         if (driveRoots[i] instanceof Win32ShellFolder2) {
   346                         if (driveRoots != null) {
   323                             Win32ShellFolder2 sf = (Win32ShellFolder2)driveRoots[i];
   347                             for (int i = 0; i < driveRoots.length; i++) {
   324                             if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
   348                                 if (driveRoots[i] instanceof Win32ShellFolder2) {
   325                                 return new File(sf.getPath());
   349                                     Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];
       
   350                                     if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
       
   351                                         return new File(sf.getPath());
       
   352                                     }
       
   353                                 }
   326                             }
   354                             }
   327                         }
   355                         }
   328                     }
   356                     }
   329                 }
   357                     // Ouch, we have no hard drives. Return something "valid" anyway.
   330             }
   358                     return new File("C:\\");
   331             // Ouch, we have no hard drives. Return something "valid" anyway.
   359                 }
   332             return new File("C:\\");
   360             }
   333         }
   361         });
   334     }
   362     }
   335 
   363 
   336 
   364 
   337     /**
   365     /**
   338      * Finalizer to clean up any COM objects or PIDLs used by this object.
   366      * Finalizer to clean up any COM objects or PIDLs used by this object.
   362     // Release a PIDL object
   390     // Release a PIDL object
   363     // Needs to be accessible to Win32ShellFolderManager2
   391     // Needs to be accessible to Win32ShellFolderManager2
   364     static native void releasePIDL(long pIDL);
   392     static native void releasePIDL(long pIDL);
   365 
   393 
   366     // Release an IShellFolder object
   394     // Release an IShellFolder object
       
   395     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   367     private static native void releaseIShellFolder(long pIShellFolder);
   396     private static native void releaseIShellFolder(long pIShellFolder);
   368 
   397 
   369     /**
   398     /**
   370      * Accessor for IShellFolder
   399      * Accessor for IShellFolder
   371      */
   400      */
   372     public long getIShellFolder() {
   401     public long getIShellFolder() {
   373         if (disposer.pIShellFolder == 0) {
   402         if (disposer.pIShellFolder == 0) {
   374             assert(isDirectory());
   403             disposer.pIShellFolder =
   375             assert(parent != null);
   404                 ShellFolder.getInvoker().invoke(new Callable<Long>() {
   376             long parentIShellFolder = getParentIShellFolder();
   405                     public Long call() throws Exception {
   377             if (parentIShellFolder == 0) {
   406                         assert(isDirectory());
   378                 throw new InternalError("Parent IShellFolder was null for " + getAbsolutePath());
   407                         assert(parent != null);
   379             }
   408                         long parentIShellFolder = getParentIShellFolder();
   380             // We are a directory with a parent and a relative PIDL.
   409                         if (parentIShellFolder == 0) {
   381             // We want to bind to the parent so we get an IShellFolder instance associated with us.
   410                             throw new InternalError("Parent IShellFolder was null for "
   382             disposer.pIShellFolder = bindToObject(parentIShellFolder, disposer.relativePIDL);
   411                                     + getAbsolutePath());
   383             if (disposer.pIShellFolder == 0) {
   412                         }
   384                 throw new InternalError("Unable to bind " + getAbsolutePath() + " to parent");
   413                         // We are a directory with a parent and a relative PIDL.
   385             }
   414                         // We want to bind to the parent so we get an
       
   415                         // IShellFolder instance associated with us.
       
   416                         long pIShellFolder = bindToObject(parentIShellFolder,
       
   417                                 disposer.relativePIDL);
       
   418                         if (pIShellFolder == 0) {
       
   419                             throw new InternalError("Unable to bind "
       
   420                                     + getAbsolutePath() + " to parent");
       
   421                         }
       
   422                         return pIShellFolder;
       
   423                     }
       
   424                 });
   386         }
   425         }
   387         return disposer.pIShellFolder;
   426         return disposer.pIShellFolder;
   388     }
   427     }
   389 
   428 
   390     /**
   429     /**
   470         }
   509         }
   471 
   510 
   472         return false;
   511         return false;
   473     }
   512     }
   474 
   513 
   475     private static boolean pidlsEqual(long pIShellFolder, long pidl1, long pidl2) {
   514     private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2) {
   476         return (compareIDs(pIShellFolder, pidl1, pidl2) == 0);
   515         return ShellFolder.getInvoker().invoke(new Callable<Boolean>() {
   477     }
   516             public Boolean call() throws Exception {
       
   517                 return (compareIDs(pIShellFolder, pidl1, pidl2) == 0);
       
   518             }
       
   519         });
       
   520     }
       
   521 
       
   522     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   478     private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);
   523     private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);
   479 
   524 
       
   525     private Boolean cachedIsFileSystem;
       
   526 
   480     /**
   527     /**
   481      * @return Whether this is a file system shell folder
   528      * @return Whether this is a file system shell folder
   482      */
   529      */
   483     public boolean isFileSystem() {
   530     public synchronized boolean isFileSystem() {
   484         return hasAttribute(ATTRIB_FILESYSTEM);
   531         if (cachedIsFileSystem == null) {
       
   532             cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);
       
   533         }
       
   534 
       
   535         return cachedIsFileSystem;
   485     }
   536     }
   486 
   537 
   487     /**
   538     /**
   488      * Return whether the given attribute flag is set for this object
   539      * Return whether the given attribute flag is set for this object
   489      */
   540      */
   490     public boolean hasAttribute(int attribute) {
   541     public boolean hasAttribute(final int attribute) {
   491         // Caching at this point doesn't seem to be cost efficient
   542         return ShellFolder.getInvoker().invoke(new Callable<Boolean>() {
   492         return (getAttributes0(getParentIShellFolder(), getRelativePIDL(), attribute) & attribute) != 0;
   543             public Boolean call() throws Exception {
       
   544                 // Caching at this point doesn't seem to be cost efficient
       
   545                 return (getAttributes0(getParentIShellFolder(),
       
   546                         getRelativePIDL(), attribute)
       
   547                         & attribute) != 0;
       
   548             }
       
   549         });
   493     }
   550     }
   494 
   551 
   495     /**
   552     /**
   496      * Returns the queried attributes specified in attrsMask.
   553      * Returns the queried attributes specified in attrsMask.
   497      *
   554      *
   498      * Could plausibly be used for attribute caching but have to be
   555      * Could plausibly be used for attribute caching but have to be
   499      * very careful not to touch network drives and file system roots
   556      * very careful not to touch network drives and file system roots
   500      * with a full attrsMask
   557      * with a full attrsMask
   501      */
   558      * NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
       
   559      */
       
   560 
   502     private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
   561     private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
   503 
   562 
   504     // Return the path to the underlying file system object
   563     // Return the path to the underlying file system object
   505     private static String getFileSystemPath(long parentIShellFolder, long relativePIDL) {
   564     private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
   506         int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
   565         return ShellFolder.getInvoker().invoke(new Callable<String>() {
   507         if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
   566             public String call() throws Exception {
   508             getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
   567                 int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
   509 
   568                 if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
   510             String s =
   569                         getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
   511                 getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
   570 
   512                                   getLinkLocation(parentIShellFolder, relativePIDL, false));
   571                     String s =
   513             if (s != null && s.startsWith("\\\\")) {
   572                             getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
   514                 return s;
   573                                     getLinkLocation(parentIShellFolder, relativePIDL, false));
   515             }
   574                     if (s != null && s.startsWith("\\\\")) {
   516         }
   575                         return s;
   517         return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_NORMAL | SHGDN_FORPARSING);
   576                     }
   518     }
   577                 }
       
   578                 return getDisplayNameOf(parentIShellFolder, relativePIDL, SHGDN_FORPARSING);
       
   579             }
       
   580         });
       
   581     }
       
   582 
   519     // Needs to be accessible to Win32ShellFolderManager2
   583     // Needs to be accessible to Win32ShellFolderManager2
   520     static native String getFileSystemPath(int csidl) throws IOException;
   584     static String getFileSystemPath(final int csidl) throws IOException {
       
   585         return ShellFolder.getInvoker().invoke(new Callable<String>() {
       
   586             public String call() throws Exception {
       
   587                 return getFileSystemPath0(csidl);
       
   588             }
       
   589         });
       
   590     }
       
   591 
       
   592     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
       
   593     private static native String getFileSystemPath0(int csidl) throws IOException;
   521 
   594 
   522     // Return whether the path is a network root.
   595     // Return whether the path is a network root.
   523     // Path is assumed to be non-null
   596     // Path is assumed to be non-null
   524     private static boolean isNetworkRoot(String path) {
   597     private static boolean isNetworkRoot(String path) {
   525         return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));
   598         return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));
   555     /*
   628     /*
   556      * Functions for enumerating an IShellFolder's children
   629      * Functions for enumerating an IShellFolder's children
   557      */
   630      */
   558     // Returns an IEnumIDList interface for an IShellFolder.  The value
   631     // Returns an IEnumIDList interface for an IShellFolder.  The value
   559     // returned must be released using releaseEnumObjects().
   632     // returned must be released using releaseEnumObjects().
   560     private long getEnumObjects(long pIShellFolder, boolean includeHiddenFiles) {
   633     private long getEnumObjects(long pIShellFolder, final boolean includeHiddenFiles) {
   561         boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder());
   634         final boolean isDesktop = (disposer.pIShellFolder == getDesktopIShellFolder());
   562         return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);
   635         return ShellFolder.getInvoker().invoke(new Callable<Long>() {
   563     }
   636             public Long call() throws Exception {
       
   637                 return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);
       
   638             }
       
   639         });
       
   640     }
       
   641 
   564     // Returns an IEnumIDList interface for an IShellFolder.  The value
   642     // Returns an IEnumIDList interface for an IShellFolder.  The value
   565     // returned must be released using releaseEnumObjects().
   643     // returned must be released using releaseEnumObjects().
       
   644     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   566     private native long getEnumObjects(long pIShellFolder, boolean isDesktop,
   645     private native long getEnumObjects(long pIShellFolder, boolean isDesktop,
   567                                        boolean includeHiddenFiles);
   646                                        boolean includeHiddenFiles);
   568     // Returns the next sequential child as a relative PIDL
   647     // Returns the next sequential child as a relative PIDL
   569     // from an IEnumIDList interface.  The value returned must
   648     // from an IEnumIDList interface.  The value returned must
   570     // be released using releasePIDL().
   649     // be released using releasePIDL().
       
   650     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   571     private native long getNextChild(long pEnumObjects);
   651     private native long getNextChild(long pEnumObjects);
   572     // Releases the IEnumIDList interface
   652     // Releases the IEnumIDList interface
       
   653     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   573     private native void releaseEnumObjects(long pEnumObjects);
   654     private native void releaseEnumObjects(long pEnumObjects);
   574 
   655 
   575     // Returns the IShellFolder of a child from a parent IShellFolder
   656     // Returns the IShellFolder of a child from a parent IShellFolder
   576     // and a relative PIDL.  The value returned must be released
   657     // and a relative PIDL.  The value returned must be released
   577     // using releaseIShellFolder().
   658     // using releaseIShellFolder().
       
   659     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   578     private static native long bindToObject(long parentIShellFolder, long pIDL);
   660     private static native long bindToObject(long parentIShellFolder, long pIDL);
   579 
   661 
   580     /**
   662     /**
   581      * @return An array of shell folders that are children of this shell folder
   663      * @return An array of shell folders that are children of this shell folder
   582      *         object. The array will be empty if the folder is empty.  Returns
   664      *         object. The array will be empty if the folder is empty.  Returns
   583      *         <code>null</code> if this shellfolder does not denote a directory.
   665      *         <code>null</code> if this shellfolder does not denote a directory.
   584      */
   666      */
   585     public File[] listFiles(boolean includeHiddenFiles) {
   667     public File[] listFiles(final boolean includeHiddenFiles) {
   586         SecurityManager security = System.getSecurityManager();
   668         SecurityManager security = System.getSecurityManager();
   587         if (security != null) {
   669         if (security != null) {
   588             security.checkRead(getPath());
   670             security.checkRead(getPath());
   589         }
   671         }
   590         if (!isDirectory()) {
   672 
   591             return null;
   673         return ShellFolder.getInvoker().invoke(new Callable<File[]>() {
   592         }
   674             public File[] call() throws Exception {
   593         // Links to directories are not directories and cannot be parents.
   675                 if (!isDirectory()) {
   594         // This does not apply to folders in My Network Places (NetHood)
   676                     return null;
   595         // because they are both links and real directories!
   677                 }
   596         if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
   678                 // Links to directories are not directories and cannot be parents.
   597             return new File[0];
   679                 // This does not apply to folders in My Network Places (NetHood)
   598         }
   680                 // because they are both links and real directories!
   599 
   681                 if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
   600         Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();
       
   601         Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();
       
   602 
       
   603         // If we are a directory, we have a parent and (at least) a
       
   604         // relative PIDL. We must first ensure we are bound to the
       
   605         // parent so we have an IShellFolder to query.
       
   606         long pIShellFolder = getIShellFolder();
       
   607         // Now we can enumerate the objects in this folder.
       
   608         ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
       
   609         long pEnumObjects = getEnumObjects(pIShellFolder, includeHiddenFiles);
       
   610         if (pEnumObjects != 0) {
       
   611             long childPIDL;
       
   612             int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
       
   613             do {
       
   614                 if (Thread.currentThread().isInterrupted()) {
       
   615                     return new File[0];
   682                     return new File[0];
   616                 }
   683                 }
   617                 childPIDL = getNextChild(pEnumObjects);
   684 
   618                 boolean releasePIDL = true;
   685                 Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();
   619                 if (childPIDL != 0 &&
   686                 Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();
   620                     (getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
   687 
   621                     Win32ShellFolder2 childFolder = null;
   688                 // If we are a directory, we have a parent and (at least) a
   622                     if (this.equals(desktop)
   689                 // relative PIDL. We must first ensure we are bound to the
   623                         && personal != null
   690                 // parent so we have an IShellFolder to query.
   624                         && pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
   691                 long pIShellFolder = getIShellFolder();
   625                         childFolder = personal;
   692                 // Now we can enumerate the objects in this folder.
   626                     } else {
   693                 ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
   627                         childFolder = new Win32ShellFolder2(this, childPIDL);
   694                 long pEnumObjects = getEnumObjects(pIShellFolder, includeHiddenFiles);
   628                         releasePIDL = false;
   695                 if (pEnumObjects != 0) {
   629                     }
   696                     long childPIDL;
   630                     list.add(childFolder);
   697                     int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
   631                 }
   698                     do {
   632                 if (releasePIDL) {
   699                         childPIDL = getNextChild(pEnumObjects);
   633                     releasePIDL(childPIDL);
   700                         boolean releasePIDL = true;
   634                 }
   701                         if (childPIDL != 0 &&
   635             } while (childPIDL != 0);
   702                                 (getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
   636             releaseEnumObjects(pEnumObjects);
   703                             Win32ShellFolder2 childFolder;
   637         }
   704                             if (Win32ShellFolder2.this.equals(desktop)
   638         return list.toArray(new ShellFolder[list.size()]);
   705                                     && personal != null
       
   706                                     && pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
       
   707                                 childFolder = personal;
       
   708                             } else {
       
   709                                 childFolder = new Win32ShellFolder2(Win32ShellFolder2.this, childPIDL);
       
   710                                 releasePIDL = false;
       
   711                             }
       
   712                             list.add(childFolder);
       
   713                         }
       
   714                         if (releasePIDL) {
       
   715                             releasePIDL(childPIDL);
       
   716                         }
       
   717                     } while (childPIDL != 0 && !Thread.currentThread().isInterrupted());
       
   718                     releaseEnumObjects(pEnumObjects);
       
   719                 }
       
   720                 return Thread.currentThread().isInterrupted()
       
   721                     ? new File[0]
       
   722                     : list.toArray(new ShellFolder[list.size()]);
       
   723             }
       
   724         });
   639     }
   725     }
   640 
   726 
   641 
   727 
   642     /**
   728     /**
   643      * Look for (possibly special) child folder by it's path
   729      * Look for (possibly special) child folder by it's path
   644      *
   730      *
   645      * @return The child shellfolder, or null if not found.
   731      * @return The child shellfolder, or null if not found.
   646      */
   732      */
   647     Win32ShellFolder2 getChildByPath(String filePath) {
   733     Win32ShellFolder2 getChildByPath(final String filePath) {
   648         long pIShellFolder = getIShellFolder();
   734         return ShellFolder.getInvoker().invoke(new Callable<Win32ShellFolder2>() {
   649         long pEnumObjects =  getEnumObjects(pIShellFolder, true);
   735             public Win32ShellFolder2 call() throws Exception {
   650         Win32ShellFolder2 child = null;
   736                 long pIShellFolder = getIShellFolder();
   651         long childPIDL;
   737                 long pEnumObjects = getEnumObjects(pIShellFolder, true);
   652 
   738                 Win32ShellFolder2 child = null;
   653         while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
   739                 long childPIDL = 0;
   654             if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
   740 
   655                 String path = getFileSystemPath(pIShellFolder, childPIDL);
   741                 while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
   656                 if (path != null && path.equalsIgnoreCase(filePath)) {
   742                     if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
   657                     long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
   743                         String path = getFileSystemPath(pIShellFolder, childPIDL);
   658                     child = new Win32ShellFolder2(this, childIShellFolder, childPIDL, path);
   744                         if (path != null && path.equalsIgnoreCase(filePath)) {
   659                     break;
   745                             long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
   660                 }
   746                             child = new Win32ShellFolder2(Win32ShellFolder2.this,
   661             }
   747                                     childIShellFolder, childPIDL, path);
   662             releasePIDL(childPIDL);
   748                             break;
   663         }
   749                         }
   664         releaseEnumObjects(pEnumObjects);
   750                     }
   665         return child;
   751                     releasePIDL(childPIDL);
   666     }
   752                 }
   667 
   753                 releaseEnumObjects(pEnumObjects);
       
   754                 return child;
       
   755             }
       
   756         });
       
   757     }
       
   758 
       
   759     private Boolean cachedIsLink;
   668 
   760 
   669     /**
   761     /**
   670      * @return Whether this shell folder is a link
   762      * @return Whether this shell folder is a link
   671      */
   763      */
   672     public boolean isLink() {
   764     public synchronized boolean isLink() {
   673         return hasAttribute(ATTRIB_LINK);
   765         if (cachedIsLink == null) {
       
   766             cachedIsLink = hasAttribute(ATTRIB_LINK);
       
   767         }
       
   768 
       
   769         return cachedIsLink;
   674     }
   770     }
   675 
   771 
   676     /**
   772     /**
   677      * @return Whether this shell folder is marked as hidden
   773      * @return Whether this shell folder is marked as hidden
   678      */
   774      */
   680         return hasAttribute(ATTRIB_HIDDEN);
   776         return hasAttribute(ATTRIB_HIDDEN);
   681     }
   777     }
   682 
   778 
   683 
   779 
   684     // Return the link location of a shell folder
   780     // Return the link location of a shell folder
       
   781     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   685     private static native long getLinkLocation(long parentIShellFolder,
   782     private static native long getLinkLocation(long parentIShellFolder,
   686                                         long relativePIDL, boolean resolve);
   783                                         long relativePIDL, boolean resolve);
   687 
   784 
   688     /**
   785     /**
   689      * @return The shell folder linked to by this shell folder, or null
   786      * @return The shell folder linked to by this shell folder, or null
   691      */
   788      */
   692     public ShellFolder getLinkLocation()  {
   789     public ShellFolder getLinkLocation()  {
   693         return getLinkLocation(true);
   790         return getLinkLocation(true);
   694     }
   791     }
   695 
   792 
   696     private ShellFolder getLinkLocation(boolean resolve)  {
   793     private ShellFolder getLinkLocation(final boolean resolve) {
   697         if (!isLink()) {
   794         return ShellFolder.getInvoker().invoke(new Callable<ShellFolder>() {
   698             return null;
   795             public ShellFolder call() throws Exception {
   699         }
   796                 if (!isLink()) {
   700 
   797                     return null;
   701         ShellFolder location = null;
   798                 }
   702         long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
   799 
   703                                                 getRelativePIDL(), resolve);
   800                 ShellFolder location = null;
   704         if (linkLocationPIDL != 0) {
   801                 long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
   705             try {
   802                         getRelativePIDL(), resolve);
   706                 location =
   803                 if (linkLocationPIDL != 0) {
   707                     Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
   804                     try {
   708                                                                                linkLocationPIDL);
   805                         location =
   709             } catch (InternalError e) {
   806                                 Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
   710                 // Could be a link to a non-bindable object, such as a network connection
   807                                         linkLocationPIDL);
   711                 // TODO: getIShellFolder() should throw FileNotFoundException instead
   808                     } catch (InternalError e) {
   712             }
   809                         // Could be a link to a non-bindable object, such as a network connection
   713         }
   810                         // TODO: getIShellFolder() should throw FileNotFoundException instead
   714         return location;
   811                     }
       
   812                 }
       
   813                 return location;
       
   814             }
       
   815         });
   715     }
   816     }
   716 
   817 
   717     // Parse a display name into a PIDL relative to the current IShellFolder.
   818     // Parse a display name into a PIDL relative to the current IShellFolder.
   718     long parseDisplayName(String name) throws FileNotFoundException {
   819     long parseDisplayName(final String name) throws FileNotFoundException {
   719         try {
   820         try {
   720             return parseDisplayName0(getIShellFolder(), name);
   821             return ShellFolder.getInvoker().invoke(new Callable<Long>() {
   721         } catch (IOException e) {
   822                 public Long call() throws Exception {
   722             throw new FileNotFoundException("Could not find file " + name);
   823                     return parseDisplayName0(getIShellFolder(), name);
   723         }
   824                 }
   724     }
   825             });
       
   826         } catch (RuntimeException e) {
       
   827             if (e.getCause() instanceof IOException) {
       
   828                 throw new FileNotFoundException("Could not find file " + name);
       
   829             }
       
   830             throw e;
       
   831         }
       
   832     }
       
   833 
       
   834     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   725     private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;
   835     private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;
   726 
   836 
   727     // Return the display name of a shell folder
   837     // Return the display name of a shell folder
       
   838     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   728     private static native String getDisplayNameOf(long parentIShellFolder,
   839     private static native String getDisplayNameOf(long parentIShellFolder,
   729                                                   long relativePIDL,
   840                                                   long relativePIDL,
   730                                                   int attrs);
   841                                                   int attrs);
   731 
   842 
   732     /**
   843     /**
   733      * @return The name used to display this shell folder
   844      * @return The name used to display this shell folder
   734      */
   845      */
   735     public String getDisplayName() {
   846     public String getDisplayName() {
   736         if (displayName == null) {
   847         if (displayName == null) {
   737             displayName = getDisplayNameOf(getParentIShellFolder(), getRelativePIDL(), SHGDN_NORMAL);
   848             displayName =
       
   849                 ShellFolder.getInvoker().invoke(new Callable<String>() {
       
   850                     public String call() throws Exception {
       
   851                         return getDisplayNameOf(getParentIShellFolder(),
       
   852                                 getRelativePIDL(), SHGDN_NORMAL);
       
   853                     }
       
   854                 });
   738         }
   855         }
   739         return displayName;
   856         return displayName;
   740     }
   857     }
   741 
   858 
   742     // Return the folder type of a shell folder
   859     // Return the folder type of a shell folder
       
   860     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   743     private static native String getFolderType(long pIDL);
   861     private static native String getFolderType(long pIDL);
   744 
   862 
   745     /**
   863     /**
   746      * @return The type of shell folder as a string
   864      * @return The type of shell folder as a string
   747      */
   865      */
   748     public String getFolderType() {
   866     public String getFolderType() {
   749         if (folderType == null) {
   867         if (folderType == null) {
   750             folderType = getFolderType(getAbsolutePIDL());
   868             final long absolutePIDL = getAbsolutePIDL();
       
   869             folderType =
       
   870                 ShellFolder.getInvoker().invoke(new Callable<String>() {
       
   871                     public String call() throws Exception {
       
   872                         return getFolderType(absolutePIDL);
       
   873                     }
       
   874                 });
   751         }
   875         }
   752         return folderType;
   876         return folderType;
   753     }
   877     }
   754 
   878 
   755     // Return the executable type of a file system shell folder
   879     // Return the executable type of a file system shell folder
   772     private static Map smallSystemImages = new HashMap();
   896     private static Map smallSystemImages = new HashMap();
   773     private static Map largeSystemImages = new HashMap();
   897     private static Map largeSystemImages = new HashMap();
   774     private static Map smallLinkedSystemImages = new HashMap();
   898     private static Map smallLinkedSystemImages = new HashMap();
   775     private static Map largeLinkedSystemImages = new HashMap();
   899     private static Map largeLinkedSystemImages = new HashMap();
   776 
   900 
       
   901     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   777     private static native long getIShellIcon(long pIShellFolder);
   902     private static native long getIShellIcon(long pIShellFolder);
       
   903 
       
   904     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   778     private static native int getIconIndex(long parentIShellIcon, long relativePIDL);
   905     private static native int getIconIndex(long parentIShellIcon, long relativePIDL);
   779 
   906 
   780     // Return the icon of a file system shell folder in the form of an HICON
   907     // Return the icon of a file system shell folder in the form of an HICON
   781     private static native long getIcon(String absolutePath, boolean getLargeIcon);
   908     private static native long getIcon(String absolutePath, boolean getLargeIcon);
       
   909 
       
   910     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
   782     private static native long extractIcon(long parentIShellFolder, long relativePIDL,
   911     private static native long extractIcon(long parentIShellFolder, long relativePIDL,
   783                                            boolean getLargeIcon);
   912                                            boolean getLargeIcon);
   784 
   913 
   785     // Returns an icon from the Windows system icon list in the form of an HICON
   914     // Returns an icon from the Windows system icon list in the form of an HICON
   786     private static native long getSystemIcon(int iconID);
   915     private static native long getSystemIcon(int iconID);
   797 
   926 
   798     public static native int[] getFileChooserBitmapBits();
   927     public static native int[] getFileChooserBitmapBits();
   799 
   928 
   800     private long getIShellIcon() {
   929     private long getIShellIcon() {
   801         if (pIShellIcon == -1L) {
   930         if (pIShellIcon == -1L) {
   802             pIShellIcon = getIShellIcon(getIShellFolder());
   931             pIShellIcon =
       
   932                 ShellFolder.getInvoker().invoke(new Callable<Long>() {
       
   933                     public Long call() throws Exception {
       
   934                         return getIShellIcon(getIShellFolder());
       
   935                     }
       
   936                 });
   803         }
   937         }
   804         return pIShellIcon;
   938         return pIShellIcon;
   805     }
   939     }
   806 
   940 
   807 
   941 
   848 
   982 
   849 
   983 
   850     /**
   984     /**
   851      * @return The icon image used to display this shell folder
   985      * @return The icon image used to display this shell folder
   852      */
   986      */
   853     public Image getIcon(boolean getLargeIcon) {
   987     public Image getIcon(final boolean getLargeIcon) {
   854         Image icon = getLargeIcon ? largeIcon : smallIcon;
   988         Image icon = getLargeIcon ? largeIcon : smallIcon;
   855         if (icon == null) {
   989         if (icon == null) {
   856             long parentIShellIcon = (parent != null) ? ((Win32ShellFolder2)parent).getIShellIcon() : 0L;
   990             icon =
   857             long relativePIDL = getRelativePIDL();
   991                 ShellFolder.getInvoker().invoke(new Callable<Image>() {
   858 
   992                     public Image call() throws Exception {
   859             if (isFileSystem()) {
   993                         Image newIcon = null;
   860                 // These are cached per type (using the index in the system image list)
   994                         if (isFileSystem()) {
   861                 int index = getIconIndex(parentIShellIcon, relativePIDL);
   995                             long parentIShellIcon = (parent != null)
   862                 if (index > 0) {
   996                                 ? ((Win32ShellFolder2) parent).getIShellIcon()
   863                     Map imageCache;
   997                                 : 0L;
   864                     if (isLink()) {
   998                             long relativePIDL = getRelativePIDL();
   865                         imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
   999 
   866                     } else {
  1000                             // These are cached per type (using the index in the system image list)
   867                         imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
  1001                             int index = getIconIndex(parentIShellIcon, relativePIDL);
   868                     }
  1002                             if (index > 0) {
   869                     icon = (Image)imageCache.get(Integer.valueOf(index));
  1003                                 Map imageCache;
   870                     if (icon == null) {
  1004                                 if (isLink()) {
   871                         long hIcon = getIcon(getAbsolutePath(), getLargeIcon);
  1005                                     imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
   872                         icon = makeIcon(hIcon, getLargeIcon);
  1006                                 } else {
   873                         disposeIcon(hIcon);
  1007                                     imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
   874                         if (icon != null) {
  1008                                 }
   875                             imageCache.put(Integer.valueOf(index), icon);
  1009                                 newIcon = (Image) imageCache.get(Integer.valueOf(index));
       
  1010                                 if (newIcon == null) {
       
  1011                                     long hIcon = getIcon(getAbsolutePath(), getLargeIcon);
       
  1012                                     newIcon = makeIcon(hIcon, getLargeIcon);
       
  1013                                     disposeIcon(hIcon);
       
  1014                                     if (newIcon != null) {
       
  1015                                         imageCache.put(Integer.valueOf(index), newIcon);
       
  1016                                     }
       
  1017                                 }
       
  1018                             }
   876                         }
  1019                         }
   877                     }
  1020 
   878                 }
  1021                         if (newIcon == null) {
   879             }
  1022                             // These are only cached per object
   880 
  1023                             long hIcon = extractIcon(getParentIShellFolder(),
   881             if (icon == null) {
  1024                                 getRelativePIDL(), getLargeIcon);
   882                 // These are only cached per object
  1025                             newIcon = makeIcon(hIcon, getLargeIcon);
   883                 long hIcon = extractIcon(getParentIShellFolder(), getRelativePIDL(), getLargeIcon);
  1026                             disposeIcon(hIcon);
   884                 icon = makeIcon(hIcon, getLargeIcon);
  1027                         }
   885                 disposeIcon(hIcon);
  1028 
   886             }
  1029                         if (newIcon == null) {
   887 
  1030                             newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);
       
  1031                         }
       
  1032                         return newIcon;
       
  1033                     }
       
  1034                 });
   888             if (getLargeIcon) {
  1035             if (getLargeIcon) {
   889                 largeIcon = icon;
  1036                 largeIcon = icon;
   890             } else {
  1037             } else {
   891                 smallIcon = icon;
  1038                 smallIcon = icon;
   892             }
  1039             }
   893         }
       
   894         if (icon == null) {
       
   895             icon = super.getIcon(getLargeIcon);
       
   896         }
  1040         }
   897         return icon;
  1041         return icon;
   898     }
  1042     }
   899 
  1043 
   900     /**
  1044     /**
   967     private static final int LVCFMT_LEFT = 0;
  1111     private static final int LVCFMT_LEFT = 0;
   968     private static final int LVCFMT_RIGHT = 1;
  1112     private static final int LVCFMT_RIGHT = 1;
   969     private static final int LVCFMT_CENTER = 2;
  1113     private static final int LVCFMT_CENTER = 2;
   970 
  1114 
   971     public ShellFolderColumnInfo[] getFolderColumns() {
  1115     public ShellFolderColumnInfo[] getFolderColumns() {
   972         ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
  1116         return ShellFolder.getInvoker().invoke(new Callable<ShellFolderColumnInfo[]>() {
   973 
  1117             public ShellFolderColumnInfo[] call() throws Exception {
   974         if (columns != null) {
  1118                 ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
   975             List<ShellFolderColumnInfo> notNullColumns =
  1119 
   976                     new ArrayList<ShellFolderColumnInfo>();
  1120                 if (columns != null) {
   977             for (int i = 0; i < columns.length; i++) {
  1121                     List<ShellFolderColumnInfo> notNullColumns =
   978                 ShellFolderColumnInfo column = columns[i];
  1122                             new ArrayList<ShellFolderColumnInfo>();
   979                 if (column != null) {
  1123                     for (int i = 0; i < columns.length; i++) {
   980                     column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
  1124                         ShellFolderColumnInfo column = columns[i];
   981                             ? SwingConstants.RIGHT
  1125                         if (column != null) {
   982                                 : column.getAlignment() == LVCFMT_CENTER
  1126                             column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
   983                             ? SwingConstants.CENTER
  1127                                     ? SwingConstants.RIGHT
   984                                 : SwingConstants.LEADING);
  1128                                     : column.getAlignment() == LVCFMT_CENTER
   985 
  1129                                     ? SwingConstants.CENTER
   986                     column.setComparator(new ColumnComparator(getIShellFolder(), i));
  1130                                     : SwingConstants.LEADING);
   987 
  1131 
   988                     notNullColumns.add(column);
  1132                             column.setComparator(new ColumnComparator(getIShellFolder(), i));
   989                 }
  1133 
   990             }
  1134                             notNullColumns.add(column);
   991             columns = new ShellFolderColumnInfo[notNullColumns.size()];
  1135                         }
   992             notNullColumns.toArray(columns);
  1136                     }
   993         }
  1137                     columns = new ShellFolderColumnInfo[notNullColumns.size()];
   994         return columns;
  1138                     notNullColumns.toArray(columns);
   995     }
  1139                 }
   996 
  1140                 return columns;
   997     public Object getFolderColumnValue(int column) {
  1141             }
   998         return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
  1142         });
   999     }
  1143     }
  1000 
  1144 
       
  1145     public Object getFolderColumnValue(final int column) {
       
  1146         return ShellFolder.getInvoker().invoke(new Callable<Object>() {
       
  1147             public Object call() throws Exception {
       
  1148                 return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
       
  1149             }
       
  1150         });
       
  1151     }
       
  1152 
       
  1153     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  1001     private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
  1154     private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
  1002 
  1155 
       
  1156     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  1003     private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
  1157     private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
  1004 
  1158 
       
  1159     // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
  1005     private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
  1160     private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
  1006 
  1161 
  1007 
  1162 
  1008     public void sortChildren(List<? extends File> files) {
  1163     public void sortChildren(List<? extends File> files) {
  1009         Collections.sort(files, new ColumnComparator(getIShellFolder(), 0));
  1164         Collections.sort(files, new ColumnComparator(getIShellFolder(), 0));
  1018             this.parentIShellFolder = parentIShellFolder;
  1173             this.parentIShellFolder = parentIShellFolder;
  1019             this.columnIdx = columnIdx;
  1174             this.columnIdx = columnIdx;
  1020         }
  1175         }
  1021 
  1176 
  1022         // compares 2 objects within this folder by the specified column
  1177         // compares 2 objects within this folder by the specified column
  1023         public int compare(File o, File o1) {
  1178         public int compare(final File o, final File o1) {
  1024             if (o instanceof Win32ShellFolder2
  1179             return ShellFolder.getInvoker().invoke(new Callable<Integer>() {
  1025                     && o1 instanceof Win32ShellFolder2) {
  1180                 public Integer call() throws Exception {
  1026                 // delegates comparison to native method
  1181                     if (o instanceof Win32ShellFolder2
  1027                 return compareIDsByColumn(parentIShellFolder,
  1182                             && o1 instanceof Win32ShellFolder2) {
  1028                         ((Win32ShellFolder2) o).getRelativePIDL(),
  1183                         // delegates comparison to native method
  1029                         ((Win32ShellFolder2) o1).getRelativePIDL(),
  1184                         return compareIDsByColumn(parentIShellFolder,
  1030                         columnIdx);
  1185                                 ((Win32ShellFolder2) o).getRelativePIDL(),
  1031             }
  1186                                 ((Win32ShellFolder2) o1).getRelativePIDL(),
  1032             return 0;
  1187                                 columnIdx);
  1033         }
  1188                     }
  1034     }
  1189                     return 0;
  1035 
  1190                 }
       
  1191             });
       
  1192         }
       
  1193     }
  1036 }
  1194 }