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
216 });
217 disposed = true;
218 }
219 }
220 FolderDisposer disposer = new FolderDisposer();
221 private void setIShellFolder(long pIShellFolder) {
222 disposer.pIShellFolder = pIShellFolder;
223 }
224 private void setRelativePIDL(long relativePIDL) {
225 disposer.relativePIDL = relativePIDL;
226 }
227 /*
228 * The following are for caching various shell folder properties.
229 */
230 private long pIShellIcon = -1L;
231 private String folderType = null;
232 private String displayName = null;
233 private Image smallIcon = null;
234 private Image largeIcon = null;
235 private Boolean isDir = null;
236
237 /*
238 * The following is to identify the My Documents folder as being special
239 */
240 private boolean isPersonal;
241
242 private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {
243 String path = getFileSystemPath(csidl);
244 return path == null
245 ? ("ShellFolder: 0x" + Integer.toHexString(csidl))
246 : path;
247 }
248
249 /**
250 * Create a system special shell folder, such as the
251 * desktop or Network Neighborhood.
252 */
253 Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {
254 // Desktop is parent of DRIVES and NETWORK, not necessarily
255 // other special shell folders.
256 super(null, composePathForCsidl(csidl));
257
258 invoke(new Callable<Void>() {
259 public Void call() throws InterruptedException {
260 if (csidl == DESKTOP) {
261 initDesktop();
262 } else {
263 initSpecial(getDesktop().getIShellFolder(), csidl);
264 // At this point, the native method initSpecial() has set our relativePIDL
265 // relative to the Desktop, which may not be our immediate parent. We need
266 // to traverse this ID list and break it into a chain of shell folders from
267 // the top, with each one having an immediate parent and a relativePIDL
268 // relative to that parent.
269 long pIDL = disposer.relativePIDL;
270 parent = getDesktop();
271 while (pIDL != 0) {
272 // Get a child pidl relative to 'parent'
273 long childPIDL = copyFirstPIDLEntry(pIDL);
274 if (childPIDL != 0) {
275 // Get a handle to the rest of the ID list
276 // i,e, parent's grandchilren and down
277 pIDL = getNextPIDLEntry(pIDL);
278 if (pIDL != 0) {
279 // Now we know that parent isn't immediate to 'this' because it
280 // has a continued ID list. Create a shell folder for this child
281 // pidl and make it the new 'parent'.
282 parent = new Win32ShellFolder2((Win32ShellFolder2) parent, childPIDL);
283 } else {
284 // No grandchildren means we have arrived at the parent of 'this',
285 // and childPIDL is directly relative to parent.
286 disposer.relativePIDL = childPIDL;
287 }
288 } else {
289 break;
290 }
291 }
292 }
293 return null;
294 }
295 }, InterruptedException.class);
296
297 sun.java2d.Disposer.addRecord(this, disposer);
298 }
299
300
301 /**
302 * Create a system shell folder
303 */
304 Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path) {
305 super(parent, (path != null) ? path : "ShellFolder: ");
306 this.disposer.pIShellFolder = pIShellFolder;
307 this.disposer.relativePIDL = relativePIDL;
308 sun.java2d.Disposer.addRecord(this, disposer);
309 }
310
311
312 /**
313 * Creates a shell folder with a parent and relative PIDL
314 */
315 Win32ShellFolder2(final Win32ShellFolder2 parent, final long relativePIDL) throws InterruptedException {
316 super(parent,
317 invoke(new Callable<String>() {
318 public String call() {
319 return getFileSystemPath(parent.getIShellFolder(), relativePIDL);
320 }
321 }, RuntimeException.class)
322 );
323 this.disposer.relativePIDL = relativePIDL;
324 sun.java2d.Disposer.addRecord(this, disposer);
325 }
326
327 // Initializes the desktop shell folder
328 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
329 private native void initDesktop();
330
331 // Initializes a special, non-file system shell folder
332 // from one of the above constants
333 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
334 private native void initSpecial(long desktopIShellFolder, int csidl);
335
336 /** Marks this folder as being the My Documents (Personal) folder */
337 public void setIsPersonal() {
338 isPersonal = true;
339 }
340
341 /**
342 * This method is implemented to make sure that no instances
343 * of <code>ShellFolder</code> are ever serialized. If <code>isFileSystem()</code> returns
344 * <code>true</code>, then the object is representable with an instance of
584 */
585
586 private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
587
588 // Return the path to the underlying file system object
589 // Should be called from the COM thread
590 private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
591 int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
592 if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
593 getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
594
595 String s =
596 getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
597 getLinkLocation(parentIShellFolder, relativePIDL, false));
598 if (s != null && s.startsWith("\\\\")) {
599 return s;
600 }
601 }
602 String path = getDisplayNameOf(parentIShellFolder, relativePIDL,
603 SHGDN_FORPARSING);
604 // if this is a library its default save location is taken as a path
605 // this is a temp fix until java.io starts support Libraries
606 if( path != null && path.startsWith("::{") &&
607 path.toLowerCase().endsWith(".library-ms")) {
608 for (KnownFolderDefinition kf : KnownFolderDefinition.libraries) {
609 if( path.toLowerCase().endsWith(
610 kf.relativePath.toLowerCase()) &&
611 path.toUpperCase().startsWith(
612 kf.parsingName.substring(0, 40).toUpperCase()) ) {
613 return kf.saveLocation;
614 }
615 }
616 }
617 return path;
618 }
619
620 // Needs to be accessible to Win32ShellFolderManager2
621 static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {
622 String path = invoke(new Callable<String>() {
623 public String call() throws IOException {
624 return getFileSystemPath0(csidl);
625 }
626 }, IOException.class);
627 if (path != null) {
628 SecurityManager security = System.getSecurityManager();
629 if (security != null) {
630 security.checkRead(path);
631 }
632 }
633 return path;
634 }
635
636 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
637 private static native String getFileSystemPath0(int csidl) throws IOException;
733 // parent so we have an IShellFolder to query.
734 long pIShellFolder = getIShellFolder();
735 // Now we can enumerate the objects in this folder.
736 ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
737 long pEnumObjects = getEnumObjects(includeHiddenFiles);
738 if (pEnumObjects != 0) {
739 try {
740 long childPIDL;
741 int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
742 do {
743 childPIDL = getNextChild(pEnumObjects);
744 boolean releasePIDL = true;
745 if (childPIDL != 0 &&
746 (getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
747 Win32ShellFolder2 childFolder;
748 if (Win32ShellFolder2.this.equals(desktop)
749 && personal != null
750 && pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
751 childFolder = personal;
752 } else {
753 childFolder = new Win32ShellFolder2(Win32ShellFolder2.this, childPIDL);
754 releasePIDL = false;
755 }
756 list.add(childFolder);
757 }
758 if (releasePIDL) {
759 releasePIDL(childPIDL);
760 }
761 } while (childPIDL != 0 && !Thread.currentThread().isInterrupted());
762 } finally {
763 releaseEnumObjects(pEnumObjects);
764 }
765 }
766 return Thread.currentThread().isInterrupted()
767 ? new File[0]
768 : list.toArray(new ShellFolder[list.size()]);
769 }
770 }, InterruptedException.class);
771 } catch (InterruptedException e) {
772 return new File[0];
773 }
774 }
775
776
777 /**
778 * Look for (possibly special) child folder by it's path
779 *
780 * @return The child shellfolder, or null if not found.
781 */
782 Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {
783 return invoke(new Callable<Win32ShellFolder2>() {
784 public Win32ShellFolder2 call() throws InterruptedException {
785 long pIShellFolder = getIShellFolder();
786 long pEnumObjects = getEnumObjects(true);
787 Win32ShellFolder2 child = null;
788 long childPIDL;
789
790 while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
791 if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
792 String path = getFileSystemPath(pIShellFolder, childPIDL);
793 if (path != null && path.equalsIgnoreCase(filePath)) {
794 long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
795 child = new Win32ShellFolder2(Win32ShellFolder2.this,
796 childIShellFolder, childPIDL, path);
797 break;
798 }
799 }
800 releasePIDL(childPIDL);
801 }
802 releaseEnumObjects(pEnumObjects);
803 return child;
804 }
805 }, InterruptedException.class);
806 }
807
808 private volatile Boolean cachedIsLink;
809
810 /**
811 * @return Whether this shell folder is a link
812 */
813 public boolean isLink() {
814 if (cachedIsLink == null) {
815 cachedIsLink = hasAttribute(ATTRIB_LINK);
816 }
1112 *
1113 * @see sun.awt.shell.ShellFolder#compareTo(File)
1114 */
1115 public int compareTo(File file2) {
1116 if (!(file2 instanceof Win32ShellFolder2)) {
1117 if (isFileSystem() && !isSpecial()) {
1118 return super.compareTo(file2);
1119 } else {
1120 return -1; // Non-file shellfolders sort before files
1121 }
1122 }
1123 return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);
1124 }
1125
1126 // native constants from commctrl.h
1127 private static final int LVCFMT_LEFT = 0;
1128 private static final int LVCFMT_RIGHT = 1;
1129 private static final int LVCFMT_CENTER = 2;
1130
1131 public ShellFolderColumnInfo[] getFolderColumns() {
1132 return invoke(new Callable<ShellFolderColumnInfo[]>() {
1133 public ShellFolderColumnInfo[] call() {
1134 ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
1135
1136 if (columns != null) {
1137 List<ShellFolderColumnInfo> notNullColumns =
1138 new ArrayList<ShellFolderColumnInfo>();
1139 for (int i = 0; i < columns.length; i++) {
1140 ShellFolderColumnInfo column = columns[i];
1141 if (column != null) {
1142 column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
1143 ? SwingConstants.RIGHT
1144 : column.getAlignment() == LVCFMT_CENTER
1145 ? SwingConstants.CENTER
1146 : SwingConstants.LEADING);
1147
1148 column.setComparator(new ColumnComparator(Win32ShellFolder2.this, i));
1149
1150 notNullColumns.add(column);
1151 }
1152 }
1153 columns = new ShellFolderColumnInfo[notNullColumns.size()];
1154 notNullColumns.toArray(columns);
1155 }
1156 return columns;
1157 }
1158 });
1159 }
1160
1161 public Object getFolderColumnValue(final int column) {
1162 return invoke(new Callable<Object>() {
1163 public Object call() {
1164 return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
1165 }
1166 });
1167 }
1168
1169 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1170 private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
1171
1172 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1173 private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
1174
1175 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1176 private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
1177
1178
1179 public void sortChildren(final List<? extends File> files) {
1180 // To avoid loads of synchronizations with Invoker and improve performance we
1181 // synchronize the whole code of the sort method once
1182 invoke(new Callable<Void>() {
1183 public Void call() {
1184 Collections.sort(files, new ColumnComparator(Win32ShellFolder2.this, 0));
1185
1186 return null;
|
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.FileNotFoundException;
33 import java.io.IOException;
34 import java.util.*;
35 import java.util.concurrent.*;
36 import javax.swing.SwingConstants;
37
38 // NOTE: This class supersedes Win32ShellFolder, which was removed from
39 // distribution after version 1.4.2.
40
41 /**
42 * Win32 Shell Folders
43 * <P>
44 * <BR>
45 * There are two fundamental types of shell folders : file system folders
46 * and non-file system folders. File system folders are relatively easy
47 * to deal with. Non-file system folders are items such as My Computer,
48 * Network Neighborhood, and the desktop. Some of these non-file system
49 * folders have special values and properties.
50 * <P>
51 * <BR>
52 * Win32 keeps two basic data structures for shell folders. The first
217 });
218 disposed = true;
219 }
220 }
221 FolderDisposer disposer = new FolderDisposer();
222 private void setIShellFolder(long pIShellFolder) {
223 disposer.pIShellFolder = pIShellFolder;
224 }
225 private void setRelativePIDL(long relativePIDL) {
226 disposer.relativePIDL = relativePIDL;
227 }
228 /*
229 * The following are for caching various shell folder properties.
230 */
231 private long pIShellIcon = -1L;
232 private String folderType = null;
233 private String displayName = null;
234 private Image smallIcon = null;
235 private Image largeIcon = null;
236 private Boolean isDir = null;
237 private final boolean isLib;
238
239 /*
240 * The following is to identify the My Documents folder as being special
241 */
242 private boolean isPersonal;
243
244 private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {
245 String path = getFileSystemPath(csidl);
246 return path == null
247 ? ("ShellFolder: 0x" + Integer.toHexString(csidl))
248 : path;
249 }
250
251 /**
252 * Create a system special shell folder, such as the
253 * desktop or Network Neighborhood.
254 */
255 Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {
256 // Desktop is parent of DRIVES and NETWORK, not necessarily
257 // other special shell folders.
258 super(null, composePathForCsidl(csidl));
259 isLib = false;
260
261 invoke(new Callable<Void>() {
262 public Void call() throws InterruptedException {
263 if (csidl == DESKTOP) {
264 initDesktop();
265 } else {
266 initSpecial(getDesktop().getIShellFolder(), csidl);
267 // At this point, the native method initSpecial() has set our relativePIDL
268 // relative to the Desktop, which may not be our immediate parent. We need
269 // to traverse this ID list and break it into a chain of shell folders from
270 // the top, with each one having an immediate parent and a relativePIDL
271 // relative to that parent.
272 long pIDL = disposer.relativePIDL;
273 parent = getDesktop();
274 while (pIDL != 0) {
275 // Get a child pidl relative to 'parent'
276 long childPIDL = copyFirstPIDLEntry(pIDL);
277 if (childPIDL != 0) {
278 // Get a handle to the rest of the ID list
279 // i,e, parent's grandchilren and down
280 pIDL = getNextPIDLEntry(pIDL);
281 if (pIDL != 0) {
282 // Now we know that parent isn't immediate to 'this' because it
283 // has a continued ID list. Create a shell folder for this child
284 // pidl and make it the new 'parent'.
285 parent = createShellFolder((Win32ShellFolder2) parent, childPIDL);
286 } else {
287 // No grandchildren means we have arrived at the parent of 'this',
288 // and childPIDL is directly relative to parent.
289 disposer.relativePIDL = childPIDL;
290 }
291 } else {
292 break;
293 }
294 }
295 }
296 return null;
297 }
298 }, InterruptedException.class);
299
300 sun.java2d.Disposer.addRecord(this, disposer);
301 }
302
303
304 /**
305 * Create a system shell folder
306 */
307 Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path, boolean isLib) {
308 super(parent, (path != null) ? path : "ShellFolder: ");
309 this.isLib = isLib;
310 this.disposer.pIShellFolder = pIShellFolder;
311 this.disposer.relativePIDL = relativePIDL;
312 sun.java2d.Disposer.addRecord(this, disposer);
313 }
314
315
316 /**
317 * Creates a shell folder with a parent and relative PIDL
318 */
319 static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, long pIDL)
320 throws InterruptedException {
321 String path = invoke(new Callable<String>() {
322 public String call() {
323 return getFileSystemPath(parent.getIShellFolder(), pIDL);
324 }
325 }, RuntimeException.class);
326 String libPath = resolveLibrary(path);
327 if (libPath == null) {
328 return new Win32ShellFolder2(parent, 0, pIDL, path, false);
329 } else {
330 return new Win32ShellFolder2(parent, 0, pIDL, libPath, true);
331 }
332 }
333
334 // Initializes the desktop shell folder
335 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
336 private native void initDesktop();
337
338 // Initializes a special, non-file system shell folder
339 // from one of the above constants
340 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
341 private native void initSpecial(long desktopIShellFolder, int csidl);
342
343 /** Marks this folder as being the My Documents (Personal) folder */
344 public void setIsPersonal() {
345 isPersonal = true;
346 }
347
348 /**
349 * This method is implemented to make sure that no instances
350 * of <code>ShellFolder</code> are ever serialized. If <code>isFileSystem()</code> returns
351 * <code>true</code>, then the object is representable with an instance of
591 */
592
593 private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
594
595 // Return the path to the underlying file system object
596 // Should be called from the COM thread
597 private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
598 int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
599 if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
600 getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
601
602 String s =
603 getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
604 getLinkLocation(parentIShellFolder, relativePIDL, false));
605 if (s != null && s.startsWith("\\\\")) {
606 return s;
607 }
608 }
609 String path = getDisplayNameOf(parentIShellFolder, relativePIDL,
610 SHGDN_FORPARSING);
611 return path;
612 }
613
614 private static String resolveLibrary(String path) {
615 // if this is a library its default save location is taken as a path
616 // this is a temp fix until java.io starts support Libraries
617 if( path != null && path.startsWith("::{") &&
618 path.toLowerCase().endsWith(".library-ms")) {
619 for (KnownFolderDefinition kf : KnownFolderDefinition.libraries) {
620 if (path.toLowerCase().endsWith(
621 "\\" + kf.relativePath.toLowerCase()) &&
622 path.toUpperCase().startsWith(
623 kf.parsingName.substring(0, 40).toUpperCase())) {
624 return kf.saveLocation;
625 }
626 }
627 }
628 return null;
629 }
630
631 // Needs to be accessible to Win32ShellFolderManager2
632 static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {
633 String path = invoke(new Callable<String>() {
634 public String call() throws IOException {
635 return getFileSystemPath0(csidl);
636 }
637 }, IOException.class);
638 if (path != null) {
639 SecurityManager security = System.getSecurityManager();
640 if (security != null) {
641 security.checkRead(path);
642 }
643 }
644 return path;
645 }
646
647 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
648 private static native String getFileSystemPath0(int csidl) throws IOException;
744 // parent so we have an IShellFolder to query.
745 long pIShellFolder = getIShellFolder();
746 // Now we can enumerate the objects in this folder.
747 ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
748 long pEnumObjects = getEnumObjects(includeHiddenFiles);
749 if (pEnumObjects != 0) {
750 try {
751 long childPIDL;
752 int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
753 do {
754 childPIDL = getNextChild(pEnumObjects);
755 boolean releasePIDL = true;
756 if (childPIDL != 0 &&
757 (getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
758 Win32ShellFolder2 childFolder;
759 if (Win32ShellFolder2.this.equals(desktop)
760 && personal != null
761 && pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
762 childFolder = personal;
763 } else {
764 childFolder = createShellFolder(Win32ShellFolder2.this, childPIDL);
765 releasePIDL = false;
766 }
767 list.add(childFolder);
768 }
769 if (releasePIDL) {
770 releasePIDL(childPIDL);
771 }
772 } while (childPIDL != 0 && !Thread.currentThread().isInterrupted());
773 } finally {
774 releaseEnumObjects(pEnumObjects);
775 }
776 }
777 return Thread.currentThread().isInterrupted()
778 ? new File[0]
779 : list.toArray(new ShellFolder[list.size()]);
780 }
781 }, InterruptedException.class);
782 } catch (InterruptedException e) {
783 return new File[0];
784 }
785 }
786
787
788 /**
789 * Look for (possibly special) child folder by it's path
790 *
791 * @return The child shellfolder, or null if not found.
792 */
793 Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {
794 return invoke(new Callable<Win32ShellFolder2>() {
795 public Win32ShellFolder2 call() throws InterruptedException {
796 long pIShellFolder = getIShellFolder();
797 long pEnumObjects = getEnumObjects(true);
798 Win32ShellFolder2 child = null;
799 long childPIDL;
800
801 while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
802 if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
803 String path = getFileSystemPath(pIShellFolder, childPIDL);
804 if(isLib) path = resolveLibrary( path );
805 if (path != null && path.equalsIgnoreCase(filePath)) {
806 long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
807 child = new Win32ShellFolder2(Win32ShellFolder2.this,
808 childIShellFolder, childPIDL, path, isLib);
809 break;
810 }
811 }
812 releasePIDL(childPIDL);
813 }
814 releaseEnumObjects(pEnumObjects);
815 return child;
816 }
817 }, InterruptedException.class);
818 }
819
820 private volatile Boolean cachedIsLink;
821
822 /**
823 * @return Whether this shell folder is a link
824 */
825 public boolean isLink() {
826 if (cachedIsLink == null) {
827 cachedIsLink = hasAttribute(ATTRIB_LINK);
828 }
1124 *
1125 * @see sun.awt.shell.ShellFolder#compareTo(File)
1126 */
1127 public int compareTo(File file2) {
1128 if (!(file2 instanceof Win32ShellFolder2)) {
1129 if (isFileSystem() && !isSpecial()) {
1130 return super.compareTo(file2);
1131 } else {
1132 return -1; // Non-file shellfolders sort before files
1133 }
1134 }
1135 return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);
1136 }
1137
1138 // native constants from commctrl.h
1139 private static final int LVCFMT_LEFT = 0;
1140 private static final int LVCFMT_RIGHT = 1;
1141 private static final int LVCFMT_CENTER = 2;
1142
1143 public ShellFolderColumnInfo[] getFolderColumns() {
1144 ShellFolder library = resolveLibrary();
1145 if (library != null) return library.getFolderColumns();
1146 return invoke(new Callable<ShellFolderColumnInfo[]>() {
1147 public ShellFolderColumnInfo[] call() {
1148 ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
1149
1150 if (columns != null) {
1151 List<ShellFolderColumnInfo> notNullColumns =
1152 new ArrayList<ShellFolderColumnInfo>();
1153 for (int i = 0; i < columns.length; i++) {
1154 ShellFolderColumnInfo column = columns[i];
1155 if (column != null) {
1156 column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
1157 ? SwingConstants.RIGHT
1158 : column.getAlignment() == LVCFMT_CENTER
1159 ? SwingConstants.CENTER
1160 : SwingConstants.LEADING);
1161
1162 column.setComparator(new ColumnComparator(Win32ShellFolder2.this, i));
1163
1164 notNullColumns.add(column);
1165 }
1166 }
1167 columns = new ShellFolderColumnInfo[notNullColumns.size()];
1168 notNullColumns.toArray(columns);
1169 }
1170 return columns;
1171 }
1172 });
1173 }
1174
1175 public Object getFolderColumnValue(final int column) {
1176 if(!isLibrary()) {
1177 ShellFolder library = resolveLibrary();
1178 if (library != null) return library.getFolderColumnValue(column);
1179 }
1180 return invoke(new Callable<Object>() {
1181 public Object call() {
1182 return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
1183 }
1184 });
1185 }
1186
1187 boolean isLibrary() {
1188 return isLib;
1189 }
1190
1191 private ShellFolder resolveLibrary() {
1192 for (ShellFolder f = this; f != null; f = f.parent) {
1193 if (!f.isFileSystem()) {
1194 if (f instanceof Win32ShellFolder2 &&
1195 ((Win32ShellFolder2)f).isLibrary()) {
1196 try {
1197 return getShellFolder(new File(getPath()));
1198 } catch (FileNotFoundException e) {
1199 }
1200 }
1201 break;
1202 }
1203 }
1204 return null;
1205 }
1206
1207 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1208 private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
1209
1210 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1211 private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
1212
1213 // NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1214 private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
1215
1216
1217 public void sortChildren(final List<? extends File> files) {
1218 // To avoid loads of synchronizations with Invoker and improve performance we
1219 // synchronize the whole code of the sort method once
1220 invoke(new Callable<Void>() {
1221 public Void call() {
1222 Collections.sort(files, new ColumnComparator(Win32ShellFolder2.this, 0));
1223
1224 return null;
|