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