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