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 |
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 */ |
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 |
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)); |