Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/java.desktop/windows/classes/sun/awt/shell/Win32ShellFolder2.java
66646 views
1
/*
2
* Copyright (c) 2003, 2021, 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.AbstractMultiResolutionImage;
31
import java.awt.image.BufferedImage;
32
import java.awt.image.ImageObserver;
33
import java.awt.image.MultiResolutionImage;
34
import java.io.File;
35
import java.io.FileNotFoundException;
36
import java.io.IOException;
37
import java.io.Serial;
38
import java.util.ArrayList;
39
import java.util.Arrays;
40
import java.util.Collections;
41
import java.util.Comparator;
42
import java.util.HashMap;
43
import java.util.List;
44
import java.util.Map;
45
import java.util.concurrent.Callable;
46
47
import javax.swing.SwingConstants;
48
49
// NOTE: This class supersedes Win32ShellFolder, which was removed from
50
// distribution after version 1.4.2.
51
52
/**
53
* Win32 Shell Folders
54
* <P>
55
* <BR>
56
* There are two fundamental types of shell folders : file system folders
57
* and non-file system folders. File system folders are relatively easy
58
* to deal with. Non-file system folders are items such as My Computer,
59
* Network Neighborhood, and the desktop. Some of these non-file system
60
* folders have special values and properties.
61
* <P>
62
* <BR>
63
* Win32 keeps two basic data structures for shell folders. The first
64
* of these is called an ITEMIDLIST. Usually a pointer, called an
65
* LPITEMIDLIST, or more frequently just "PIDL". This structure holds
66
* a series of identifiers and can be either relative to the desktop
67
* (an absolute PIDL), or relative to the shell folder that contains them.
68
* Some Win32 functions can take absolute or relative PIDL values, and
69
* others can only accept relative values.
70
* <BR>
71
* The second data structure is an IShellFolder COM interface. Using
72
* this interface, one can enumerate the relative PIDLs in a shell
73
* folder, get attributes, etc.
74
* <BR>
75
* All Win32ShellFolder2 objects which are folder types (even non-file
76
* system folders) contain an IShellFolder object. Files are named in
77
* directories via relative PIDLs.
78
*
79
* @author Michael Martak
80
* @author Leif Samuelsson
81
* @author Kenneth Russell
82
* @since 1.4 */
83
@SuppressWarnings("serial") // JDK-implementation class
84
final class Win32ShellFolder2 extends ShellFolder {
85
86
static final int SMALL_ICON_SIZE = 16;
87
static final int LARGE_ICON_SIZE = 32;
88
static final int MIN_QUALITY_ICON = 16;
89
static final int MAX_QUALITY_ICON = 256;
90
private final static int[] ICON_RESOLUTIONS
91
= {16, 24, 32, 48, 64, 72, 96, 128, 256};
92
93
static final int FILE_ICON_ID = 1;
94
static final int FOLDER_ICON_ID = 4;
95
96
private static native void initIDs();
97
98
static {
99
initIDs();
100
}
101
102
// Win32 Shell Folder Constants
103
public static final int DESKTOP = 0x0000;
104
public static final int INTERNET = 0x0001;
105
public static final int PROGRAMS = 0x0002;
106
public static final int CONTROLS = 0x0003;
107
public static final int PRINTERS = 0x0004;
108
public static final int PERSONAL = 0x0005;
109
public static final int FAVORITES = 0x0006;
110
public static final int STARTUP = 0x0007;
111
public static final int RECENT = 0x0008;
112
public static final int SENDTO = 0x0009;
113
public static final int BITBUCKET = 0x000a;
114
public static final int STARTMENU = 0x000b;
115
public static final int DESKTOPDIRECTORY = 0x0010;
116
public static final int DRIVES = 0x0011;
117
public static final int NETWORK = 0x0012;
118
public static final int NETHOOD = 0x0013;
119
public static final int FONTS = 0x0014;
120
public static final int TEMPLATES = 0x0015;
121
public static final int COMMON_STARTMENU = 0x0016;
122
public static final int COMMON_PROGRAMS = 0X0017;
123
public static final int COMMON_STARTUP = 0x0018;
124
public static final int COMMON_DESKTOPDIRECTORY = 0x0019;
125
public static final int APPDATA = 0x001a;
126
public static final int PRINTHOOD = 0x001b;
127
public static final int ALTSTARTUP = 0x001d;
128
public static final int COMMON_ALTSTARTUP = 0x001e;
129
public static final int COMMON_FAVORITES = 0x001f;
130
public static final int INTERNET_CACHE = 0x0020;
131
public static final int COOKIES = 0x0021;
132
public static final int HISTORY = 0x0022;
133
134
// Win32 shell folder attributes
135
public static final int ATTRIB_CANCOPY = 0x00000001;
136
public static final int ATTRIB_CANMOVE = 0x00000002;
137
public static final int ATTRIB_CANLINK = 0x00000004;
138
public static final int ATTRIB_CANRENAME = 0x00000010;
139
public static final int ATTRIB_CANDELETE = 0x00000020;
140
public static final int ATTRIB_HASPROPSHEET = 0x00000040;
141
public static final int ATTRIB_DROPTARGET = 0x00000100;
142
public static final int ATTRIB_LINK = 0x00010000;
143
public static final int ATTRIB_SHARE = 0x00020000;
144
public static final int ATTRIB_READONLY = 0x00040000;
145
public static final int ATTRIB_GHOSTED = 0x00080000;
146
public static final int ATTRIB_HIDDEN = 0x00080000;
147
public static final int ATTRIB_FILESYSANCESTOR = 0x10000000;
148
public static final int ATTRIB_FOLDER = 0x20000000;
149
public static final int ATTRIB_FILESYSTEM = 0x40000000;
150
public static final int ATTRIB_HASSUBFOLDER = 0x80000000;
151
public static final int ATTRIB_VALIDATE = 0x01000000;
152
public static final int ATTRIB_REMOVABLE = 0x02000000;
153
public static final int ATTRIB_COMPRESSED = 0x04000000;
154
public static final int ATTRIB_BROWSABLE = 0x08000000;
155
public static final int ATTRIB_NONENUMERATED = 0x00100000;
156
public static final int ATTRIB_NEWCONTENT = 0x00200000;
157
158
// IShellFolder::GetDisplayNameOf constants
159
public static final int SHGDN_NORMAL = 0;
160
public static final int SHGDN_INFOLDER = 1;
161
public static final int SHGDN_INCLUDE_NONFILESYS= 0x2000;
162
public static final int SHGDN_FORADDRESSBAR = 0x4000;
163
public static final int SHGDN_FORPARSING = 0x8000;
164
165
/** The referent to be registered with the Disposer. */
166
private Object disposerReferent = new Object();
167
168
// Values for system call LoadIcon()
169
public enum SystemIcon {
170
IDI_APPLICATION(32512),
171
IDI_HAND(32513),
172
IDI_ERROR(32513),
173
IDI_QUESTION(32514),
174
IDI_EXCLAMATION(32515),
175
IDI_WARNING(32515),
176
IDI_ASTERISK(32516),
177
IDI_INFORMATION(32516),
178
IDI_WINLOGO(32517);
179
180
private final int iconID;
181
182
SystemIcon(int iconID) {
183
this.iconID = iconID;
184
}
185
186
public int getIconID() {
187
return iconID;
188
}
189
}
190
191
// Known Folder data
192
static final class KnownFolderDefinition {
193
String guid;
194
int category;
195
String name;
196
String description;
197
String parent;
198
String relativePath;
199
String parsingName;
200
String tooltip;
201
String localizedName;
202
String icon;
203
String security;
204
long attributes;
205
int defenitionFlags;
206
String ftidType;
207
String path;
208
String saveLocation;
209
}
210
211
static final class KnownLibraries {
212
static final List<KnownFolderDefinition> INSTANCE = getLibraries();
213
}
214
215
static class FolderDisposer implements sun.java2d.DisposerRecord {
216
/*
217
* This is cached as a concession to getFolderType(), which needs
218
* an absolute PIDL.
219
*/
220
long absolutePIDL;
221
/*
222
* We keep track of shell folders through the IShellFolder
223
* interface of their parents plus their relative PIDL.
224
*/
225
long pIShellFolder;
226
long relativePIDL;
227
228
boolean disposed;
229
public void dispose() {
230
if (disposed) return;
231
invoke(new Callable<Void>() {
232
public Void call() {
233
if (relativePIDL != 0) {
234
releasePIDL(relativePIDL);
235
}
236
if (absolutePIDL != 0) {
237
releasePIDL(absolutePIDL);
238
}
239
if (pIShellFolder != 0) {
240
releaseIShellFolder(pIShellFolder);
241
}
242
return null;
243
}
244
});
245
disposed = true;
246
}
247
}
248
FolderDisposer disposer = new FolderDisposer();
249
private void setIShellFolder(long pIShellFolder) {
250
disposer.pIShellFolder = pIShellFolder;
251
}
252
private void setRelativePIDL(long relativePIDL) {
253
disposer.relativePIDL = relativePIDL;
254
}
255
/*
256
* The following are for caching various shell folder properties.
257
*/
258
private long pIShellIcon = -1L;
259
private String folderType = null;
260
private String displayName = null;
261
private Image smallIcon = null;
262
private Image largeIcon = null;
263
private Boolean isDir = null;
264
private final boolean isLib;
265
private static final String FNAME = COLUMN_NAME;
266
private static final String FSIZE = COLUMN_SIZE;
267
private static final String FTYPE = "FileChooser.fileTypeHeaderText";
268
private static final String FDATE = COLUMN_DATE;
269
270
/*
271
* The following is to identify the My Documents folder as being special
272
*/
273
private boolean isPersonal;
274
275
private static String composePathForCsidl(int csidl) throws IOException, InterruptedException {
276
String path = getFileSystemPath(csidl);
277
return path == null
278
? ("ShellFolder: 0x" + Integer.toHexString(csidl))
279
: path;
280
}
281
282
/**
283
* Create a system special shell folder, such as the
284
* desktop or Network Neighborhood.
285
*/
286
Win32ShellFolder2(final int csidl) throws IOException, InterruptedException {
287
// Desktop is parent of DRIVES and NETWORK, not necessarily
288
// other special shell folders.
289
super(null, composePathForCsidl(csidl));
290
isLib = false;
291
292
invoke(new Callable<Void>() {
293
public Void call() throws InterruptedException {
294
if (csidl == DESKTOP) {
295
initDesktop();
296
} else {
297
initSpecial(getDesktop().getIShellFolder(), csidl);
298
// At this point, the native method initSpecial() has set our relativePIDL
299
// relative to the Desktop, which may not be our immediate parent. We need
300
// to traverse this ID list and break it into a chain of shell folders from
301
// the top, with each one having an immediate parent and a relativePIDL
302
// relative to that parent.
303
long pIDL = disposer.relativePIDL;
304
parent = getDesktop();
305
while (pIDL != 0) {
306
// Get a child pidl relative to 'parent'
307
long childPIDL = copyFirstPIDLEntry(pIDL);
308
if (childPIDL != 0) {
309
// Get a handle to the rest of the ID list
310
// i,e, parent's grandchilren and down
311
pIDL = getNextPIDLEntry(pIDL);
312
if (pIDL != 0) {
313
// Now we know that parent isn't immediate to 'this' because it
314
// has a continued ID list. Create a shell folder for this child
315
// pidl and make it the new 'parent'.
316
parent = createShellFolder((Win32ShellFolder2) parent, childPIDL);
317
} else {
318
// No grandchildren means we have arrived at the parent of 'this',
319
// and childPIDL is directly relative to parent.
320
disposer.relativePIDL = childPIDL;
321
}
322
} else {
323
break;
324
}
325
}
326
}
327
return null;
328
}
329
}, InterruptedException.class);
330
331
sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer);
332
}
333
334
335
/**
336
* Create a system shell folder
337
*/
338
Win32ShellFolder2(Win32ShellFolder2 parent, long pIShellFolder, long relativePIDL, String path, boolean isLib) {
339
super(parent, (path != null) ? path : "ShellFolder: ");
340
this.isLib = isLib;
341
this.disposer.pIShellFolder = pIShellFolder;
342
this.disposer.relativePIDL = relativePIDL;
343
sun.java2d.Disposer.addObjectRecord(disposerReferent, disposer);
344
}
345
346
347
/**
348
* Creates a shell folder with a parent and relative PIDL
349
*/
350
static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, long pIDL)
351
throws InterruptedException {
352
String path = invoke(new Callable<String>() {
353
public String call() {
354
return getFileSystemPath(parent.getIShellFolder(), pIDL);
355
}
356
}, RuntimeException.class);
357
String libPath = resolveLibrary(path);
358
if (libPath == null) {
359
return new Win32ShellFolder2(parent, 0, pIDL, path, false);
360
} else {
361
return new Win32ShellFolder2(parent, 0, pIDL, libPath, true);
362
}
363
}
364
365
// Initializes the desktop shell folder
366
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
367
private native void initDesktop();
368
369
// Initializes a special, non-file system shell folder
370
// from one of the above constants
371
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
372
private native void initSpecial(long desktopIShellFolder, int csidl);
373
374
/** Marks this folder as being the My Documents (Personal) folder */
375
public void setIsPersonal() {
376
isPersonal = true;
377
}
378
379
/**
380
* This method is implemented to make sure that no instances
381
* of {@code ShellFolder} are ever serialized. If {@code isFileSystem()} returns
382
* {@code true}, then the object is representable with an instance of
383
* {@code java.io.File} instead. If not, then the object depends
384
* on native PIDL state and should not be serialized.
385
*
386
* @return a {@code java.io.File} replacement object. If the folder
387
* is a not a normal directory, then returns the first non-removable
388
* drive (normally "C:\").
389
*/
390
@Serial
391
protected Object writeReplace() throws java.io.ObjectStreamException {
392
return invoke(new Callable<File>() {
393
public File call() {
394
if (isFileSystem()) {
395
return new File(getPath());
396
} else {
397
Win32ShellFolder2 drives = Win32ShellFolderManager2.getDrives();
398
if (drives != null) {
399
File[] driveRoots = drives.listFiles();
400
if (driveRoots != null) {
401
for (int i = 0; i < driveRoots.length; i++) {
402
if (driveRoots[i] instanceof Win32ShellFolder2) {
403
Win32ShellFolder2 sf = (Win32ShellFolder2) driveRoots[i];
404
if (sf.isFileSystem() && !sf.hasAttribute(ATTRIB_REMOVABLE)) {
405
return new File(sf.getPath());
406
}
407
}
408
}
409
}
410
}
411
// Ouch, we have no hard drives. Return something "valid" anyway.
412
return new File("C:\\");
413
}
414
}
415
});
416
}
417
418
419
/**
420
* Finalizer to clean up any COM objects or PIDLs used by this object.
421
*/
422
protected void dispose() {
423
disposer.dispose();
424
}
425
426
427
// Given a (possibly multi-level) relative PIDL (with respect to
428
// the desktop, at least in all of the usage cases in this code),
429
// return a pointer to the next entry. Does not mutate the PIDL in
430
// any way. Returns 0 if the null terminator is reached.
431
// Needs to be accessible to Win32ShellFolderManager2
432
static native long getNextPIDLEntry(long pIDL);
433
434
// Given a (possibly multi-level) relative PIDL (with respect to
435
// the desktop, at least in all of the usage cases in this code),
436
// copy the first entry into a newly-allocated PIDL. Returns 0 if
437
// the PIDL is at the end of the list.
438
// Needs to be accessible to Win32ShellFolderManager2
439
static native long copyFirstPIDLEntry(long pIDL);
440
441
// Given a parent's absolute PIDL and our relative PIDL, build an absolute PIDL
442
private static native long combinePIDLs(long ppIDL, long pIDL);
443
444
// Release a PIDL object
445
// Needs to be accessible to Win32ShellFolderManager2
446
static native void releasePIDL(long pIDL);
447
448
// Release an IShellFolder object
449
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
450
private static native void releaseIShellFolder(long pIShellFolder);
451
452
/**
453
* Accessor for IShellFolder
454
*/
455
private long getIShellFolder() {
456
if (disposer.pIShellFolder == 0) {
457
try {
458
disposer.pIShellFolder = invoke(new Callable<Long>() {
459
public Long call() {
460
assert(isDirectory());
461
assert(parent != null);
462
long parentIShellFolder = getParentIShellFolder();
463
if (parentIShellFolder == 0) {
464
throw new InternalError("Parent IShellFolder was null for "
465
+ getAbsolutePath());
466
}
467
// We are a directory with a parent and a relative PIDL.
468
// We want to bind to the parent so we get an
469
// IShellFolder instance associated with us.
470
long pIShellFolder = bindToObject(parentIShellFolder,
471
disposer.relativePIDL);
472
if (pIShellFolder == 0) {
473
throw new InternalError("Unable to bind "
474
+ getAbsolutePath() + " to parent");
475
}
476
return pIShellFolder;
477
}
478
}, RuntimeException.class);
479
} catch (InterruptedException e) {
480
// Ignore error
481
}
482
}
483
return disposer.pIShellFolder;
484
}
485
486
/**
487
* Get the parent ShellFolder's IShellFolder interface
488
*/
489
public long getParentIShellFolder() {
490
Win32ShellFolder2 parent = (Win32ShellFolder2)getParentFile();
491
if (parent == null) {
492
// Parent should only be null if this is the desktop, whose
493
// relativePIDL is relative to its own IShellFolder.
494
return getIShellFolder();
495
}
496
return parent.getIShellFolder();
497
}
498
499
/**
500
* Accessor for relative PIDL
501
*/
502
public long getRelativePIDL() {
503
if (disposer.relativePIDL == 0) {
504
throw new InternalError("Should always have a relative PIDL");
505
}
506
return disposer.relativePIDL;
507
}
508
509
private long getAbsolutePIDL() {
510
if (parent == null) {
511
// This is the desktop
512
return getRelativePIDL();
513
} else {
514
if (disposer.absolutePIDL == 0) {
515
disposer.absolutePIDL = combinePIDLs(((Win32ShellFolder2)parent).getAbsolutePIDL(), getRelativePIDL());
516
}
517
518
return disposer.absolutePIDL;
519
}
520
}
521
522
/**
523
* Helper function to return the desktop
524
*/
525
public Win32ShellFolder2 getDesktop() {
526
return Win32ShellFolderManager2.getDesktop();
527
}
528
529
/**
530
* Helper function to return the desktop IShellFolder interface
531
*/
532
public long getDesktopIShellFolder() {
533
return getDesktop().getIShellFolder();
534
}
535
536
private static boolean pathsEqual(String path1, String path2) {
537
// Same effective implementation as Win32FileSystem
538
return path1.equalsIgnoreCase(path2);
539
}
540
541
/**
542
* Check to see if two ShellFolder objects are the same
543
*/
544
public boolean equals(Object o) {
545
if (o == null || !(o instanceof Win32ShellFolder2)) {
546
// Short-circuit circuitous delegation path
547
if (!(o instanceof File)) {
548
return super.equals(o);
549
}
550
return pathsEqual(getPath(), ((File) o).getPath());
551
}
552
Win32ShellFolder2 rhs = (Win32ShellFolder2) o;
553
if ((parent == null && rhs.parent != null) ||
554
(parent != null && rhs.parent == null)) {
555
return false;
556
}
557
558
if (isFileSystem() && rhs.isFileSystem()) {
559
// Only folders with identical parents can be equal
560
return (pathsEqual(getPath(), rhs.getPath()) &&
561
(parent == rhs.parent || parent.equals(rhs.parent)));
562
}
563
564
if (parent == rhs.parent || parent.equals(rhs.parent)) {
565
try {
566
return pidlsEqual(getParentIShellFolder(), disposer.relativePIDL, rhs.disposer.relativePIDL);
567
} catch (InterruptedException e) {
568
return false;
569
}
570
}
571
572
return false;
573
}
574
575
private static boolean pidlsEqual(final long pIShellFolder, final long pidl1, final long pidl2)
576
throws InterruptedException {
577
return invoke(new Callable<Boolean>() {
578
public Boolean call() {
579
return compareIDs(pIShellFolder, pidl1, pidl2) == 0;
580
}
581
}, RuntimeException.class);
582
}
583
584
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
585
private static native int compareIDs(long pParentIShellFolder, long pidl1, long pidl2);
586
587
private volatile Boolean cachedIsFileSystem;
588
589
/**
590
* @return Whether this is a file system shell folder
591
*/
592
public boolean isFileSystem() {
593
if (cachedIsFileSystem == null) {
594
cachedIsFileSystem = hasAttribute(ATTRIB_FILESYSTEM);
595
}
596
597
return cachedIsFileSystem;
598
}
599
600
/**
601
* Return whether the given attribute flag is set for this object
602
*/
603
public boolean hasAttribute(final int attribute) {
604
Boolean result = invoke(new Callable<Boolean>() {
605
public Boolean call() {
606
// Caching at this point doesn't seem to be cost efficient
607
return (getAttributes0(getParentIShellFolder(),
608
getRelativePIDL(), attribute)
609
& attribute) != 0;
610
}
611
});
612
613
return result != null && result;
614
}
615
616
/**
617
* Returns the queried attributes specified in attrsMask.
618
*
619
* Could plausibly be used for attribute caching but have to be
620
* very careful not to touch network drives and file system roots
621
* with a full attrsMask
622
* NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
623
*/
624
625
private static native int getAttributes0(long pParentIShellFolder, long pIDL, int attrsMask);
626
627
// Return the path to the underlying file system object
628
// Should be called from the COM thread
629
private static String getFileSystemPath(final long parentIShellFolder, final long relativePIDL) {
630
int linkedFolder = ATTRIB_LINK | ATTRIB_FOLDER;
631
if (parentIShellFolder == Win32ShellFolderManager2.getNetwork().getIShellFolder() &&
632
getAttributes0(parentIShellFolder, relativePIDL, linkedFolder) == linkedFolder) {
633
634
String s =
635
getFileSystemPath(Win32ShellFolderManager2.getDesktop().getIShellFolder(),
636
getLinkLocation(parentIShellFolder, relativePIDL, false));
637
if (s != null && s.startsWith("\\\\")) {
638
return s;
639
}
640
}
641
String path = getDisplayNameOf(parentIShellFolder, relativePIDL,
642
SHGDN_FORPARSING);
643
return path;
644
}
645
646
private static String resolveLibrary(String path) {
647
// if this is a library its default save location is taken as a path
648
// this is a temp fix until java.io starts support Libraries
649
if( path != null && path.startsWith("::{") &&
650
path.toLowerCase().endsWith(".library-ms")) {
651
for (KnownFolderDefinition kf : KnownLibraries.INSTANCE) {
652
if (path.toLowerCase().endsWith(
653
"\\" + kf.relativePath.toLowerCase()) &&
654
path.toUpperCase().startsWith(
655
kf.parsingName.substring(0, 40).toUpperCase())) {
656
return kf.saveLocation;
657
}
658
}
659
}
660
return null;
661
}
662
663
// Needs to be accessible to Win32ShellFolderManager2
664
static String getFileSystemPath(final int csidl) throws IOException, InterruptedException {
665
String path = invoke(new Callable<String>() {
666
public String call() throws IOException {
667
return getFileSystemPath0(csidl);
668
}
669
}, IOException.class);
670
if (path != null) {
671
@SuppressWarnings("removal")
672
SecurityManager security = System.getSecurityManager();
673
if (security != null) {
674
security.checkRead(path);
675
}
676
}
677
return path;
678
}
679
680
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
681
private static native String getFileSystemPath0(int csidl) throws IOException;
682
683
// Return whether the path is a network root.
684
// Path is assumed to be non-null
685
private static boolean isNetworkRoot(String path) {
686
return (path.equals("\\\\") || path.equals("\\") || path.equals("//") || path.equals("/"));
687
}
688
689
/**
690
* @return The parent shell folder of this shell folder, null if
691
* there is no parent
692
*/
693
public File getParentFile() {
694
return parent;
695
}
696
697
public boolean isDirectory() {
698
if (isDir == null) {
699
// Folders with SFGAO_BROWSABLE have "shell extension" handlers and are
700
// not traversable in JFileChooser.
701
if (hasAttribute(ATTRIB_FOLDER) && !hasAttribute(ATTRIB_BROWSABLE)) {
702
isDir = Boolean.TRUE;
703
} else if (isLink()) {
704
ShellFolder linkLocation = getLinkLocation(false);
705
isDir = Boolean.valueOf(linkLocation != null && linkLocation.isDirectory());
706
} else {
707
isDir = Boolean.FALSE;
708
}
709
}
710
return isDir.booleanValue();
711
}
712
713
/*
714
* Functions for enumerating an IShellFolder's children
715
*/
716
// Returns an IEnumIDList interface for an IShellFolder. The value
717
// returned must be released using releaseEnumObjects().
718
private long getEnumObjects(final boolean includeHiddenFiles) throws InterruptedException {
719
return invoke(new Callable<Long>() {
720
public Long call() {
721
boolean isDesktop = disposer.pIShellFolder == getDesktopIShellFolder();
722
723
return getEnumObjects(disposer.pIShellFolder, isDesktop, includeHiddenFiles);
724
}
725
}, RuntimeException.class);
726
}
727
728
// Returns an IEnumIDList interface for an IShellFolder. The value
729
// returned must be released using releaseEnumObjects().
730
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
731
private native long getEnumObjects(long pIShellFolder, boolean isDesktop,
732
boolean includeHiddenFiles);
733
// Returns the next sequential child as a relative PIDL
734
// from an IEnumIDList interface. The value returned must
735
// be released using releasePIDL().
736
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
737
private native long getNextChild(long pEnumObjects);
738
// Releases the IEnumIDList interface
739
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
740
private native void releaseEnumObjects(long pEnumObjects);
741
742
// Returns the IShellFolder of a child from a parent IShellFolder
743
// and a relative PIDL. The value returned must be released
744
// using releaseIShellFolder().
745
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
746
private static native long bindToObject(long parentIShellFolder, long pIDL);
747
748
/**
749
* @return An array of shell folders that are children of this shell folder
750
* object. The array will be empty if the folder is empty. Returns
751
* {@code null} if this shellfolder does not denote a directory.
752
*/
753
public File[] listFiles(final boolean includeHiddenFiles) {
754
@SuppressWarnings("removal")
755
SecurityManager security = System.getSecurityManager();
756
if (security != null) {
757
security.checkRead(getPath());
758
}
759
760
try {
761
File[] files = invoke(new Callable<File[]>() {
762
public File[] call() throws InterruptedException {
763
if (!isDirectory()) {
764
return null;
765
}
766
// Links to directories are not directories and cannot be parents.
767
// This does not apply to folders in My Network Places (NetHood)
768
// because they are both links and real directories!
769
if (isLink() && !hasAttribute(ATTRIB_FOLDER)) {
770
return new File[0];
771
}
772
773
Win32ShellFolder2 desktop = Win32ShellFolderManager2.getDesktop();
774
Win32ShellFolder2 personal = Win32ShellFolderManager2.getPersonal();
775
776
// If we are a directory, we have a parent and (at least) a
777
// relative PIDL. We must first ensure we are bound to the
778
// parent so we have an IShellFolder to query.
779
long pIShellFolder = getIShellFolder();
780
// Now we can enumerate the objects in this folder.
781
ArrayList<Win32ShellFolder2> list = new ArrayList<Win32ShellFolder2>();
782
long pEnumObjects = getEnumObjects(includeHiddenFiles);
783
if (pEnumObjects != 0) {
784
try {
785
long childPIDL;
786
int testedAttrs = ATTRIB_FILESYSTEM | ATTRIB_FILESYSANCESTOR;
787
do {
788
childPIDL = getNextChild(pEnumObjects);
789
boolean releasePIDL = true;
790
if (childPIDL != 0 &&
791
(getAttributes0(pIShellFolder, childPIDL, testedAttrs) & testedAttrs) != 0) {
792
Win32ShellFolder2 childFolder;
793
if (Win32ShellFolder2.this.equals(desktop)
794
&& personal != null
795
&& pidlsEqual(pIShellFolder, childPIDL, personal.disposer.relativePIDL)) {
796
childFolder = personal;
797
} else {
798
childFolder = createShellFolder(Win32ShellFolder2.this, childPIDL);
799
releasePIDL = false;
800
}
801
list.add(childFolder);
802
}
803
if (releasePIDL) {
804
releasePIDL(childPIDL);
805
}
806
} while (childPIDL != 0 && !Thread.currentThread().isInterrupted());
807
} finally {
808
releaseEnumObjects(pEnumObjects);
809
}
810
}
811
return Thread.currentThread().isInterrupted()
812
? new File[0]
813
: list.toArray(new ShellFolder[list.size()]);
814
}
815
}, InterruptedException.class);
816
817
return Win32ShellFolderManager2.checkFiles(files);
818
} catch (InterruptedException e) {
819
return new File[0];
820
}
821
}
822
823
824
/**
825
* Look for (possibly special) child folder by it's path
826
*
827
* @return The child shellfolder, or null if not found.
828
*/
829
Win32ShellFolder2 getChildByPath(final String filePath) throws InterruptedException {
830
return invoke(new Callable<Win32ShellFolder2>() {
831
public Win32ShellFolder2 call() throws InterruptedException {
832
long pIShellFolder = getIShellFolder();
833
long pEnumObjects = getEnumObjects(true);
834
Win32ShellFolder2 child = null;
835
long childPIDL;
836
837
while ((childPIDL = getNextChild(pEnumObjects)) != 0) {
838
if (getAttributes0(pIShellFolder, childPIDL, ATTRIB_FILESYSTEM) != 0) {
839
String path = getFileSystemPath(pIShellFolder, childPIDL);
840
if(isLib) path = resolveLibrary( path );
841
if (path != null && path.equalsIgnoreCase(filePath)) {
842
long childIShellFolder = bindToObject(pIShellFolder, childPIDL);
843
child = new Win32ShellFolder2(Win32ShellFolder2.this,
844
childIShellFolder, childPIDL, path, isLib);
845
break;
846
}
847
}
848
releasePIDL(childPIDL);
849
}
850
releaseEnumObjects(pEnumObjects);
851
return child;
852
}
853
}, InterruptedException.class);
854
}
855
856
private volatile Boolean cachedIsLink;
857
858
/**
859
* @return Whether this shell folder is a link
860
*/
861
public boolean isLink() {
862
if (cachedIsLink == null) {
863
cachedIsLink = hasAttribute(ATTRIB_LINK);
864
}
865
866
return cachedIsLink;
867
}
868
869
/**
870
* @return Whether this shell folder is marked as hidden
871
*/
872
public boolean isHidden() {
873
return hasAttribute(ATTRIB_HIDDEN);
874
}
875
876
877
// Return the link location of a shell folder
878
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
879
private static native long getLinkLocation(long parentIShellFolder,
880
long relativePIDL, boolean resolve);
881
882
/**
883
* @return The shell folder linked to by this shell folder, or null
884
* if this shell folder is not a link or is a broken or invalid link
885
*/
886
public ShellFolder getLinkLocation() {
887
return getLinkLocation(true);
888
}
889
890
private Win32ShellFolder2 getLinkLocation(final boolean resolve) {
891
return invoke(new Callable<Win32ShellFolder2>() {
892
public Win32ShellFolder2 call() {
893
if (!isLink()) {
894
return null;
895
}
896
897
Win32ShellFolder2 location = null;
898
long linkLocationPIDL = getLinkLocation(getParentIShellFolder(),
899
getRelativePIDL(), resolve);
900
if (linkLocationPIDL != 0) {
901
try {
902
location =
903
Win32ShellFolderManager2.createShellFolderFromRelativePIDL(getDesktop(),
904
linkLocationPIDL);
905
} catch (InterruptedException e) {
906
// Return null
907
} catch (InternalError e) {
908
// Could be a link to a non-bindable object, such as a network connection
909
// TODO: getIShellFolder() should throw FileNotFoundException instead
910
}
911
}
912
return location;
913
}
914
});
915
}
916
917
// Parse a display name into a PIDL relative to the current IShellFolder.
918
long parseDisplayName(final String name) throws IOException, InterruptedException {
919
return invoke(new Callable<Long>() {
920
public Long call() throws IOException {
921
return parseDisplayName0(getIShellFolder(), name);
922
}
923
}, IOException.class);
924
}
925
926
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
927
private static native long parseDisplayName0(long pIShellFolder, String name) throws IOException;
928
929
// Return the display name of a shell folder
930
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
931
private static native String getDisplayNameOf(long parentIShellFolder,
932
long relativePIDL,
933
int attrs);
934
935
// Returns data of all Known Folders registered in the system
936
private static native KnownFolderDefinition[] loadKnownFolders();
937
938
/**
939
* @return The name used to display this shell folder
940
*/
941
public String getDisplayName() {
942
if (displayName == null) {
943
displayName =
944
invoke(new Callable<String>() {
945
public String call() {
946
return getDisplayNameOf(getParentIShellFolder(),
947
getRelativePIDL(), SHGDN_NORMAL);
948
}
949
});
950
}
951
return displayName;
952
}
953
954
// Return the folder type of a shell folder
955
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
956
private static native String getFolderType(long pIDL);
957
958
/**
959
* @return The type of shell folder as a string
960
*/
961
public String getFolderType() {
962
if (folderType == null) {
963
final long absolutePIDL = getAbsolutePIDL();
964
folderType =
965
invoke(new Callable<String>() {
966
public String call() {
967
return getFolderType(absolutePIDL);
968
}
969
});
970
}
971
return folderType;
972
}
973
974
// Return the executable type of a file system shell folder
975
private native String getExecutableType(String path);
976
977
/**
978
* @return The executable type as a string
979
*/
980
public String getExecutableType() {
981
if (!isFileSystem()) {
982
return null;
983
}
984
return getExecutableType(getAbsolutePath());
985
}
986
987
988
989
// Icons
990
991
private static Map<Integer, Image> smallSystemImages = new HashMap<>();
992
private static Map<Integer, Image> largeSystemImages = new HashMap<>();
993
private static Map<Integer, Image> smallLinkedSystemImages = new HashMap<>();
994
private static Map<Integer, Image> largeLinkedSystemImages = new HashMap<>();
995
996
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
997
private static native long getIShellIcon(long pIShellFolder);
998
999
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1000
private static native int getIconIndex(long parentIShellIcon, long relativePIDL);
1001
1002
// Return the icon of a file system shell folder in the form of an HICON
1003
private static native long getIcon(String absolutePath, boolean getLargeIcon);
1004
1005
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1006
private static native long extractIcon(long parentIShellFolder, long relativePIDL,
1007
int size, boolean getDefaultIcon);
1008
1009
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1010
private static native boolean hiResIconAvailable(long parentIShellFolder, long relativePIDL);
1011
1012
// Returns an icon from the Windows system icon list in the form of an HICON
1013
private static native long getSystemIcon(int iconID);
1014
private static native long getIconResource(String libName, int iconID,
1015
int cxDesired, int cyDesired);
1016
1017
// Return the bits from an HICON. This has a side effect of setting
1018
// the imageHash variable for efficient caching / comparing.
1019
private static native int[] getIconBits(long hIcon);
1020
// Dispose the HICON
1021
private static native void disposeIcon(long hIcon);
1022
1023
// Get buttons from native toolbar implementation.
1024
static native int[] getStandardViewButton0(int iconIndex, boolean small);
1025
1026
// Should be called from the COM thread
1027
private long getIShellIcon() {
1028
if (pIShellIcon == -1L) {
1029
pIShellIcon = getIShellIcon(getIShellFolder());
1030
}
1031
1032
return pIShellIcon;
1033
}
1034
1035
private static Image makeIcon(long hIcon) {
1036
if (hIcon != 0L && hIcon != -1L) {
1037
// Get the bits. This has the side effect of setting the imageHash value for this object.
1038
final int[] iconBits = getIconBits(hIcon);
1039
if (iconBits != null) {
1040
// icons are always square
1041
final int iconSize = (int) Math.sqrt(iconBits.length);
1042
final BufferedImage img =
1043
new BufferedImage(iconSize, iconSize, BufferedImage.TYPE_INT_ARGB);
1044
img.setRGB(0, 0, iconSize, iconSize, iconBits, 0, iconSize);
1045
return img;
1046
}
1047
}
1048
return null;
1049
}
1050
1051
1052
/**
1053
* @return The icon image used to display this shell folder
1054
*/
1055
public Image getIcon(final boolean getLargeIcon) {
1056
Image icon = getLargeIcon ? largeIcon : smallIcon;
1057
int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE;
1058
if (icon == null) {
1059
icon =
1060
invoke(new Callable<Image>() {
1061
public Image call() {
1062
Image newIcon = null;
1063
Image newIcon2 = null;
1064
if (isLink()) {
1065
Win32ShellFolder2 folder = getLinkLocation(false);
1066
if (folder != null && folder.isLibrary()) {
1067
return folder.getIcon(getLargeIcon);
1068
}
1069
}
1070
if (isFileSystem() || isLibrary()) {
1071
long parentIShellIcon = (parent != null)
1072
? ((Win32ShellFolder2) parent).getIShellIcon()
1073
: 0L;
1074
long relativePIDL = getRelativePIDL();
1075
1076
// These are cached per type (using the index in the system image list)
1077
int index = getIconIndex(parentIShellIcon, relativePIDL);
1078
if (index > 0) {
1079
Map<Integer, Image> imageCache;
1080
if (isLink()) {
1081
imageCache = getLargeIcon ? largeLinkedSystemImages : smallLinkedSystemImages;
1082
} else {
1083
imageCache = getLargeIcon ? largeSystemImages : smallSystemImages;
1084
}
1085
newIcon = imageCache.get(Integer.valueOf(index));
1086
if (newIcon == null) {
1087
long hIcon = getIcon(getAbsolutePath(), getLargeIcon);
1088
newIcon = makeIcon(hIcon);
1089
disposeIcon(hIcon);
1090
if (newIcon != null) {
1091
imageCache.put(Integer.valueOf(index), newIcon);
1092
}
1093
}
1094
if (newIcon != null) {
1095
if (isLink()) {
1096
imageCache = getLargeIcon ? smallLinkedSystemImages
1097
: largeLinkedSystemImages;
1098
} else {
1099
imageCache = getLargeIcon ? smallSystemImages : largeSystemImages;
1100
}
1101
newIcon2 = imageCache.get(index);
1102
if (newIcon2 == null) {
1103
long hIcon = getIcon(getAbsolutePath(), !getLargeIcon);
1104
newIcon2 = makeIcon(hIcon);
1105
disposeIcon(hIcon);
1106
}
1107
}
1108
1109
if (newIcon2 != null) {
1110
Map<Integer, Image> bothIcons = new HashMap<>(2);
1111
bothIcons.put(getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE, newIcon);
1112
bothIcons.put(getLargeIcon ? SMALL_ICON_SIZE : LARGE_ICON_SIZE, newIcon2);
1113
newIcon = new MultiResolutionIconImage(getLargeIcon ? LARGE_ICON_SIZE
1114
: SMALL_ICON_SIZE, bothIcons);
1115
}
1116
}
1117
}
1118
1119
if (hiResIconAvailable(getParentIShellFolder(), getRelativePIDL()) || newIcon == null) {
1120
int size = getLargeIcon ? LARGE_ICON_SIZE : SMALL_ICON_SIZE;
1121
newIcon = getIcon(size, size);
1122
}
1123
1124
if (newIcon == null) {
1125
newIcon = Win32ShellFolder2.super.getIcon(getLargeIcon);
1126
}
1127
return newIcon;
1128
}
1129
});
1130
}
1131
return icon;
1132
}
1133
1134
/**
1135
* @return The icon image of specified size used to display this shell folder
1136
*/
1137
public Image getIcon(int width, int height) {
1138
int size = Math.max(width, height);
1139
return invoke(() -> {
1140
Image newIcon = null;
1141
if (isLink()) {
1142
Win32ShellFolder2 folder = getLinkLocation(false);
1143
if (folder != null && folder.isLibrary()) {
1144
return folder.getIcon(size, size);
1145
}
1146
}
1147
Map<Integer, Image> multiResolutionIcon = new HashMap<>();
1148
int start = size > MAX_QUALITY_ICON ? ICON_RESOLUTIONS.length - 1 : 0;
1149
int increment = size > MAX_QUALITY_ICON ? -1 : 1;
1150
int end = size > MAX_QUALITY_ICON ? -1 : ICON_RESOLUTIONS.length;
1151
for (int i = start; i != end; i += increment) {
1152
int s = ICON_RESOLUTIONS[i];
1153
if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON
1154
|| (s >= size && s <= size*2)) {
1155
long hIcon = extractIcon(getParentIShellFolder(),
1156
getRelativePIDL(), s, false);
1157
1158
// E_PENDING: loading can take time so get the default
1159
if (hIcon <= 0) {
1160
hIcon = extractIcon(getParentIShellFolder(),
1161
getRelativePIDL(), s, true);
1162
if (hIcon <= 0) {
1163
if (isDirectory()) {
1164
newIcon = getShell32Icon(FOLDER_ICON_ID, size);
1165
} else {
1166
newIcon = getShell32Icon(FILE_ICON_ID, size);
1167
}
1168
if (newIcon == null) {
1169
return null;
1170
}
1171
if (!(newIcon instanceof MultiResolutionImage)) {
1172
newIcon = new MultiResolutionIconImage(size, newIcon);
1173
}
1174
return newIcon;
1175
}
1176
}
1177
newIcon = makeIcon(hIcon);
1178
disposeIcon(hIcon);
1179
1180
multiResolutionIcon.put(s, newIcon);
1181
if (size < MIN_QUALITY_ICON || size > MAX_QUALITY_ICON) {
1182
break;
1183
}
1184
}
1185
}
1186
return new MultiResolutionIconImage(size, multiResolutionIcon);
1187
});
1188
}
1189
1190
/**
1191
* Gets an icon from the Windows system icon list as an {@code Image}
1192
*/
1193
static Image getSystemIcon(SystemIcon iconType) {
1194
long hIcon = getSystemIcon(iconType.getIconID());
1195
Image icon = makeIcon(hIcon);
1196
if (LARGE_ICON_SIZE != icon.getWidth(null)) {
1197
icon = new MultiResolutionIconImage(LARGE_ICON_SIZE, icon);
1198
}
1199
disposeIcon(hIcon);
1200
return icon;
1201
}
1202
1203
/**
1204
* Gets an icon from the Windows system icon list as an {@code Image}
1205
*/
1206
static Image getShell32Icon(int iconID, int size) {
1207
long hIcon = getIconResource("shell32.dll", iconID, size, size);
1208
if (hIcon != 0) {
1209
Image icon = makeIcon(hIcon);
1210
if (size != icon.getWidth(null)) {
1211
icon = new MultiResolutionIconImage(size, icon);
1212
}
1213
disposeIcon(hIcon);
1214
return icon;
1215
}
1216
return null;
1217
}
1218
1219
/**
1220
* Returns the canonical form of this abstract pathname. Equivalent to
1221
* <code>new&nbsp;Win32ShellFolder2(getParentFile(), this.{@link java.io.File#getCanonicalPath}())</code>.
1222
*
1223
* @see java.io.File#getCanonicalFile
1224
*/
1225
public File getCanonicalFile() throws IOException {
1226
return this;
1227
}
1228
1229
/*
1230
* Indicates whether this is a special folder (includes My Documents)
1231
*/
1232
public boolean isSpecial() {
1233
return isPersonal || !isFileSystem() || (this == getDesktop());
1234
}
1235
1236
/**
1237
* Compares this object with the specified object for order.
1238
*
1239
* @see sun.awt.shell.ShellFolder#compareTo(File)
1240
*/
1241
public int compareTo(File file2) {
1242
if (!(file2 instanceof Win32ShellFolder2)) {
1243
if (isFileSystem() && !isSpecial()) {
1244
return super.compareTo(file2);
1245
} else {
1246
return -1; // Non-file shellfolders sort before files
1247
}
1248
}
1249
return Win32ShellFolderManager2.compareShellFolders(this, (Win32ShellFolder2) file2);
1250
}
1251
1252
// native constants from commctrl.h
1253
private static final int LVCFMT_LEFT = 0;
1254
private static final int LVCFMT_RIGHT = 1;
1255
private static final int LVCFMT_CENTER = 2;
1256
1257
public ShellFolderColumnInfo[] getFolderColumns() {
1258
ShellFolder library = resolveLibrary();
1259
if (library != null) return library.getFolderColumns();
1260
return invoke(new Callable<ShellFolderColumnInfo[]>() {
1261
public ShellFolderColumnInfo[] call() {
1262
ShellFolderColumnInfo[] columns = doGetColumnInfo(getIShellFolder());
1263
1264
if (columns != null) {
1265
List<ShellFolderColumnInfo> notNullColumns =
1266
new ArrayList<ShellFolderColumnInfo>();
1267
for (int i = 0; i < columns.length; i++) {
1268
ShellFolderColumnInfo column = columns[i];
1269
if (column != null) {
1270
column.setAlignment(column.getAlignment() == LVCFMT_RIGHT
1271
? SwingConstants.RIGHT
1272
: column.getAlignment() == LVCFMT_CENTER
1273
? SwingConstants.CENTER
1274
: SwingConstants.LEADING);
1275
1276
column.setComparator(new ColumnComparator(Win32ShellFolder2.this, i));
1277
1278
notNullColumns.add(column);
1279
}
1280
}
1281
columns = new ShellFolderColumnInfo[notNullColumns.size()];
1282
notNullColumns.toArray(columns);
1283
}
1284
return columns;
1285
}
1286
});
1287
}
1288
1289
public Object getFolderColumnValue(final int column) {
1290
if(!isLibrary()) {
1291
ShellFolder library = resolveLibrary();
1292
if (library != null) return library.getFolderColumnValue(column);
1293
}
1294
return invoke(new Callable<Object>() {
1295
public Object call() {
1296
return doGetColumnValue(getParentIShellFolder(), getRelativePIDL(), column);
1297
}
1298
});
1299
}
1300
1301
boolean isLibrary() {
1302
return isLib;
1303
}
1304
1305
private ShellFolder resolveLibrary() {
1306
for (ShellFolder f = this; f != null; f = f.parent) {
1307
if (!f.isFileSystem()) {
1308
if (f instanceof Win32ShellFolder2 &&
1309
((Win32ShellFolder2)f).isLibrary()) {
1310
try {
1311
return getShellFolder(new File(getPath()));
1312
} catch (FileNotFoundException e) {
1313
}
1314
}
1315
break;
1316
}
1317
}
1318
return null;
1319
}
1320
1321
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1322
private native ShellFolderColumnInfo[] doGetColumnInfo(long iShellFolder2);
1323
1324
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1325
private native Object doGetColumnValue(long parentIShellFolder2, long childPIDL, int columnIdx);
1326
1327
// NOTE: this method uses COM and must be called on the 'COM thread'. See ComInvoker for the details
1328
private static native int compareIDsByColumn(long pParentIShellFolder, long pidl1, long pidl2, int columnIdx);
1329
1330
1331
public void sortChildren(final List<? extends File> files) {
1332
// To avoid loads of synchronizations with Invoker and improve performance we
1333
// synchronize the whole code of the sort method once
1334
invoke(new Callable<Void>() {
1335
public Void call() {
1336
Collections.sort(files, new ColumnComparator(Win32ShellFolder2.this, 0));
1337
1338
return null;
1339
}
1340
});
1341
}
1342
1343
private static class ColumnComparator implements Comparator<File> {
1344
private final Win32ShellFolder2 shellFolder;
1345
1346
private final int columnIdx;
1347
1348
public ColumnComparator(Win32ShellFolder2 shellFolder, int columnIdx) {
1349
this.shellFolder = shellFolder;
1350
this.columnIdx = columnIdx;
1351
}
1352
1353
// compares 2 objects within this folder by the specified column
1354
public int compare(final File o, final File o1) {
1355
Integer result = invoke(new Callable<Integer>() {
1356
public Integer call() {
1357
if (o instanceof Win32ShellFolder2
1358
&& o1 instanceof Win32ShellFolder2) {
1359
// delegates comparison to native method
1360
return compareIDsByColumn(shellFolder.getIShellFolder(),
1361
((Win32ShellFolder2) o).getRelativePIDL(),
1362
((Win32ShellFolder2) o1).getRelativePIDL(),
1363
columnIdx);
1364
}
1365
return 0;
1366
}
1367
});
1368
1369
return result == null ? 0 : result;
1370
}
1371
}
1372
1373
// Extracts libraries and their default save locations from Known Folders list
1374
private static List<KnownFolderDefinition> getLibraries() {
1375
return invoke(new Callable<List<KnownFolderDefinition>>() {
1376
@Override
1377
public List<KnownFolderDefinition> call() throws Exception {
1378
KnownFolderDefinition[] all = loadKnownFolders();
1379
List<KnownFolderDefinition> folders = new ArrayList<>();
1380
if (all != null) {
1381
for (KnownFolderDefinition kf : all) {
1382
if (kf.relativePath == null || kf.parsingName == null ||
1383
kf.saveLocation == null) {
1384
continue;
1385
}
1386
folders.add(kf);
1387
}
1388
}
1389
return folders;
1390
}
1391
});
1392
}
1393
1394
static class MultiResolutionIconImage extends AbstractMultiResolutionImage {
1395
final int baseSize;
1396
final Map<Integer, Image> resolutionVariants = new HashMap<>();
1397
1398
public MultiResolutionIconImage(int baseSize, Map<Integer, Image> resolutionVariants) {
1399
this.baseSize = baseSize;
1400
this.resolutionVariants.putAll(resolutionVariants);
1401
}
1402
1403
public MultiResolutionIconImage(int baseSize, Image image) {
1404
this.baseSize = baseSize;
1405
this.resolutionVariants.put(baseSize, image);
1406
}
1407
1408
@Override
1409
public int getWidth(ImageObserver observer) {
1410
return baseSize;
1411
}
1412
1413
@Override
1414
public int getHeight(ImageObserver observer) {
1415
return baseSize;
1416
}
1417
1418
@Override
1419
protected Image getBaseImage() {
1420
return getResolutionVariant(baseSize, baseSize);
1421
}
1422
1423
@Override
1424
public Image getResolutionVariant(double width, double height) {
1425
int dist = 0;
1426
Image retVal = null;
1427
// We only care about width since we don't support non-rectangular icons
1428
int w = (int) width;
1429
int retindex = 0;
1430
for (Integer i : resolutionVariants.keySet()) {
1431
if (retVal == null || dist > Math.abs(i - w)
1432
|| (dist == Math.abs(i - w) && i > retindex)) {
1433
retindex = i;
1434
dist = Math.abs(i - w);
1435
retVal = resolutionVariants.get(i);
1436
if (i == w) {
1437
break;
1438
}
1439
}
1440
}
1441
return retVal;
1442
}
1443
1444
@Override
1445
public List<Image> getResolutionVariants() {
1446
return Collections.unmodifiableList(
1447
new ArrayList<Image>(resolutionVariants.values()));
1448
}
1449
}
1450
}
1451
1452