1 /*
2 * Copyright (c) 2003, 2012, 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.*;
29 import java.awt.image.BufferedImage;
30
31 import java.io.File;
32 import java.io.FileNotFoundException;
33 import java.io.IOException;
34 import java.security.AccessController;
35 import java.security.PrivilegedAction;
36 import java.util.*;
37 import java.util.List;
38 import java.util.concurrent.*;
39
40 import static sun.awt.shell.Win32ShellFolder2.*;
41 import sun.awt.OSInfo;
42 import sun.misc.ThreadGroupUtils;
43
44 // NOTE: This class supersedes Win32ShellFolderManager, which was removed
45 // from distribution after version 1.4.2.
46
47 /**
48 * @author Michael Martak
49 * @author Leif Samuelsson
50 * @author Kenneth Russell
51 * @since 1.4
52 */
53
54 public class Win32ShellFolderManager2 extends ShellFolderManager {
55
56 static {
57 // Load library here
58 sun.awt.windows.WToolkit.loadLibraries();
59 }
60
61 public ShellFolder createShellFolder(File file) throws FileNotFoundException {
62 try {
63 return createShellFolder(getDesktop(), file);
64 } catch (InterruptedException e) {
65 throw new FileNotFoundException("Execution was interrupted");
66 }
67 }
68
69 static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, File file)
70 throws FileNotFoundException, InterruptedException {
71 long pIDL;
72 try {
73 pIDL = parent.parseDisplayName(file.getCanonicalPath());
74 } catch (IOException ex) {
75 pIDL = 0;
76 }
77 if (pIDL == 0) {
78 // Shouldn't happen but watch for it anyway
79 throw new FileNotFoundException("File " + file.getAbsolutePath() + " not found");
80 }
81
82 try {
83 return createShellFolderFromRelativePIDL(parent, pIDL);
84 } finally {
85 Win32ShellFolder2.releasePIDL(pIDL);
86 }
87 }
88
89 static Win32ShellFolder2 createShellFolderFromRelativePIDL(Win32ShellFolder2 parent, long pIDL)
90 throws InterruptedException {
91 // Walk down this relative pIDL, creating new nodes for each of the entries
92 while (pIDL != 0) {
93 long curPIDL = Win32ShellFolder2.copyFirstPIDLEntry(pIDL);
94 if (curPIDL != 0) {
95 parent = new Win32ShellFolder2(parent, curPIDL);
96 pIDL = Win32ShellFolder2.getNextPIDLEntry(pIDL);
97 } else {
98 // The list is empty if the parent is Desktop and pIDL is a shortcut to Desktop
99 break;
100 }
101 }
102 return parent;
103 }
104
105 private static final int VIEW_LIST = 2;
106 private static final int VIEW_DETAILS = 3;
107 private static final int VIEW_PARENTFOLDER = 8;
108 private static final int VIEW_NEWFOLDER = 11;
109
110 private static final Image[] STANDARD_VIEW_BUTTONS = new Image[12];
111
112 private static Image getStandardViewButton(int iconIndex) {
113 Image result = STANDARD_VIEW_BUTTONS[iconIndex];
114
115 if (result != null) {
116 return result;
117 }
118
119 BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
120
121 img.setRGB(0, 0, 16, 16, Win32ShellFolder2.getStandardViewButton0(iconIndex), 0, 16);
122
123 STANDARD_VIEW_BUTTONS[iconIndex] = img;
124
125 return img;
126 }
127
128 // Special folders
129 private static Win32ShellFolder2 desktop;
130 private static Win32ShellFolder2 drives;
131 private static Win32ShellFolder2 recent;
132 private static Win32ShellFolder2 network;
133 private static Win32ShellFolder2 personal;
134
135 static Win32ShellFolder2 getDesktop() {
136 if (desktop == null) {
137 try {
138 desktop = new Win32ShellFolder2(DESKTOP);
139 } catch (IOException e) {
140 // Ignore error
141 } catch (InterruptedException e) {
142 // Ignore error
143 }
144 }
145 return desktop;
146 }
147
148 static Win32ShellFolder2 getDrives() {
149 if (drives == null) {
150 try {
151 drives = new Win32ShellFolder2(DRIVES);
152 } catch (IOException e) {
153 // Ignore error
154 } catch (InterruptedException e) {
155 // Ignore error
156 }
157 }
158 return drives;
159 }
160
161 static Win32ShellFolder2 getRecent() {
162 if (recent == null) {
163 try {
164 String path = Win32ShellFolder2.getFileSystemPath(RECENT);
165 if (path != null) {
166 recent = createShellFolder(getDesktop(), new File(path));
167 }
168 } catch (InterruptedException e) {
169 // Ignore error
170 } catch (IOException e) {
171 // Ignore error
172 }
173 }
174 return recent;
175 }
176
177 static Win32ShellFolder2 getNetwork() {
178 if (network == null) {
179 try {
180 network = new Win32ShellFolder2(NETWORK);
181 } catch (IOException e) {
182 // Ignore error
183 } catch (InterruptedException e) {
184 // Ignore error
185 }
186 }
187 return network;
188 }
189
190 static Win32ShellFolder2 getPersonal() {
191 if (personal == null) {
192 try {
193 String path = Win32ShellFolder2.getFileSystemPath(PERSONAL);
194 if (path != null) {
195 Win32ShellFolder2 desktop = getDesktop();
196 personal = desktop.getChildByPath(path);
197 if (personal == null) {
198 personal = createShellFolder(getDesktop(), new File(path));
199 }
200 if (personal != null) {
201 personal.setIsPersonal();
202 }
203 }
204 } catch (InterruptedException e) {
205 // Ignore error
206 } catch (IOException e) {
207 // Ignore error
208 }
209 }
210 return personal;
211 }
212
213
214 private static File[] roots;
215
216 /**
217 * @param key a <code>String</code>
218 * "fileChooserDefaultFolder":
219 * Returns a <code>File</code> - the default shellfolder for a new filechooser
220 * "roots":
221 * Returns a <code>File[]</code> - containing the root(s) of the displayable hierarchy
222 * "fileChooserComboBoxFolders":
223 * Returns a <code>File[]</code> - an array of shellfolders representing the list to
224 * show by default in the file chooser's combobox
225 * "fileChooserShortcutPanelFolders":
226 * Returns a <code>File[]</code> - an array of shellfolders representing well-known
227 * folders, such as Desktop, Documents, History, Network, Home, etc.
228 * This is used in the shortcut panel of the filechooser on Windows 2000
229 * and Windows Me.
230 * "fileChooserIcon <icon>":
231 * Returns an <code>Image</code> - icon can be ListView, DetailsView, UpFolder, NewFolder or
232 * ViewMenu (Windows only).
233 * "optionPaneIcon iconName":
234 * Returns an <code>Image</code> - icon from the system icon list
235 *
236 * @return An Object matching the key string.
237 */
238 public Object get(String key) {
239 if (key.equals("fileChooserDefaultFolder")) {
240 File file = getPersonal();
241 if (file == null) {
242 file = getDesktop();
243 }
244 return file;
245 } else if (key.equals("roots")) {
246 // Should be "History" and "Desktop" ?
247 if (roots == null) {
248 File desktop = getDesktop();
249 if (desktop != null) {
250 roots = new File[] { desktop };
251 } else {
252 roots = (File[])super.get(key);
253 }
254 }
255 return roots;
256 } else if (key.equals("fileChooserComboBoxFolders")) {
257 Win32ShellFolder2 desktop = getDesktop();
258
259 if (desktop != null) {
260 ArrayList<File> folders = new ArrayList<File>();
261 Win32ShellFolder2 drives = getDrives();
262
263 Win32ShellFolder2 recentFolder = getRecent();
264 if (recentFolder != null && OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_2000) >= 0) {
265 folders.add(recentFolder);
266 }
267
268 folders.add(desktop);
269 // Add all second level folders
270 File[] secondLevelFolders = desktop.listFiles();
271 Arrays.sort(secondLevelFolders);
272 for (File secondLevelFolder : secondLevelFolders) {
273 Win32ShellFolder2 folder = (Win32ShellFolder2) secondLevelFolder;
274 if (!folder.isFileSystem() || (folder.isDirectory() && !folder.isLink())) {
275 folders.add(folder);
276 // Add third level for "My Computer"
277 if (folder.equals(drives)) {
278 File[] thirdLevelFolders = folder.listFiles();
279 if (thirdLevelFolders != null && thirdLevelFolders.length > 0) {
280 List<File> thirdLevelFoldersList = Arrays.asList(thirdLevelFolders);
281
282 folder.sortChildren(thirdLevelFoldersList);
283 folders.addAll(thirdLevelFoldersList);
284 }
285 }
286 }
287 }
288 return folders.toArray(new File[folders.size()]);
289 } else {
290 return super.get(key);
291 }
292 } else if (key.equals("fileChooserShortcutPanelFolders")) {
293 Toolkit toolkit = Toolkit.getDefaultToolkit();
294 ArrayList<File> folders = new ArrayList<File>();
295 int i = 0;
296 Object value;
297 do {
298 value = toolkit.getDesktopProperty("win.comdlg.placesBarPlace" + i++);
299 try {
300 if (value instanceof Integer) {
301 // A CSIDL
302 folders.add(new Win32ShellFolder2((Integer)value));
303 } else if (value instanceof String) {
304 // A path
305 folders.add(createShellFolder(new File((String)value)));
306 }
307 } catch (IOException e) {
308 // Skip this value
309 } catch (InterruptedException e) {
310 // Return empty result
311 return new File[0];
312 }
313 } while (value != null);
314
315 if (folders.size() == 0) {
316 // Use default list of places
317 for (File f : new File[] {
318 getRecent(), getDesktop(), getPersonal(), getDrives(), getNetwork()
319 }) {
320 if (f != null) {
321 folders.add(f);
322 }
323 }
324 }
325 return folders.toArray(new File[folders.size()]);
326 } else if (key.startsWith("fileChooserIcon ")) {
327 String name = key.substring(key.indexOf(" ") + 1);
328
329 int iconIndex;
330
331 if (name.equals("ListView") || name.equals("ViewMenu")) {
332 iconIndex = VIEW_LIST;
333 } else if (name.equals("DetailsView")) {
334 iconIndex = VIEW_DETAILS;
335 } else if (name.equals("UpFolder")) {
336 iconIndex = VIEW_PARENTFOLDER;
337 } else if (name.equals("NewFolder")) {
338 iconIndex = VIEW_NEWFOLDER;
339 } else {
340 return null;
341 }
342
343 return getStandardViewButton(iconIndex);
344 } else if (key.startsWith("optionPaneIcon ")) {
345 Win32ShellFolder2.SystemIcon iconType;
346 if (key == "optionPaneIcon Error") {
347 iconType = Win32ShellFolder2.SystemIcon.IDI_ERROR;
348 } else if (key == "optionPaneIcon Information") {
349 iconType = Win32ShellFolder2.SystemIcon.IDI_INFORMATION;
350 } else if (key == "optionPaneIcon Question") {
351 iconType = Win32ShellFolder2.SystemIcon.IDI_QUESTION;
352 } else if (key == "optionPaneIcon Warning") {
353 iconType = Win32ShellFolder2.SystemIcon.IDI_EXCLAMATION;
354 } else {
355 return null;
356 }
357 return Win32ShellFolder2.getSystemIcon(iconType);
358 } else if (key.startsWith("shell32Icon ") || key.startsWith("shell32LargeIcon ")) {
359 String name = key.substring(key.indexOf(" ") + 1);
360 try {
361 int i = Integer.parseInt(name);
362 if (i >= 0) {
363 return Win32ShellFolder2.getShell32Icon(i, key.startsWith("shell32LargeIcon "));
364 }
365 } catch (NumberFormatException ex) {
366 }
367 }
368 return null;
369 }
370
371 /**
372 * Does <code>dir</code> represent a "computer" such as a node on the network, or
373 * "My Computer" on the desktop.
374 */
375 public boolean isComputerNode(final File dir) {
376 if (dir != null && dir == getDrives()) {
377 return true;
378 } else {
379 String path = AccessController.doPrivileged(new PrivilegedAction<String>() {
380 public String run() {
381 return dir.getAbsolutePath();
382 }
383 });
384
385 return (path.startsWith("\\\\") && path.indexOf("\\", 2) < 0); //Network path
386 }
387 }
388
389 public boolean isFileSystemRoot(File dir) {
390 //Note: Removable drives don't "exist" but are listed in "My Computer"
391 if (dir != null) {
392 Win32ShellFolder2 drives = getDrives();
393 if (dir instanceof Win32ShellFolder2) {
394 Win32ShellFolder2 sf = (Win32ShellFolder2)dir;
395 if (sf.isFileSystem()) {
396 if (sf.parent != null) {
397 return sf.parent.equals(drives);
398 }
399 // else fall through ...
400 } else {
401 return false;
402 }
403 }
404 String path = dir.getPath();
405
406 if (path.length() != 3 || path.charAt(1) != ':') {
407 return false;
408 }
409
410 File[] files = drives.listFiles();
411
412 return files != null && Arrays.asList(files).contains(dir);
413 }
414 return false;
415 }
416
417 private static List topFolderList = null;
418 static int compareShellFolders(Win32ShellFolder2 sf1, Win32ShellFolder2 sf2) {
419 boolean special1 = sf1.isSpecial();
420 boolean special2 = sf2.isSpecial();
421
422 if (special1 || special2) {
423 if (topFolderList == null) {
424 ArrayList tmpTopFolderList = new ArrayList();
425 tmpTopFolderList.add(Win32ShellFolderManager2.getPersonal());
426 tmpTopFolderList.add(Win32ShellFolderManager2.getDesktop());
427 tmpTopFolderList.add(Win32ShellFolderManager2.getDrives());
428 tmpTopFolderList.add(Win32ShellFolderManager2.getNetwork());
429 topFolderList = tmpTopFolderList;
430 }
431 int i1 = topFolderList.indexOf(sf1);
432 int i2 = topFolderList.indexOf(sf2);
433 if (i1 >= 0 && i2 >= 0) {
434 return (i1 - i2);
435 } else if (i1 >= 0) {
436 return -1;
437 } else if (i2 >= 0) {
438 return 1;
439 }
440 }
441
442 // Non-file shellfolders sort before files
443 if (special1 && !special2) {
444 return -1;
445 } else if (special2 && !special1) {
446 return 1;
447 }
448
449 return compareNames(sf1.getAbsolutePath(), sf2.getAbsolutePath());
450 }
451
452 static int compareNames(String name1, String name2) {
453 // First ignore case when comparing
454 int diff = name1.compareToIgnoreCase(name2);
455 if (diff != 0) {
456 return diff;
457 } else {
458 // May differ in case (e.g. "mail" vs. "Mail")
459 // We need this test for consistent sorting
460 return name1.compareTo(name2);
461 }
462 }
463
464 @Override
465 protected Invoker createInvoker() {
466 return new ComInvoker();
467 }
468
469 private static class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker {
470 private static Thread comThread;
471
472 private ComInvoker() {
473 super(1, 1, 0, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
474 allowCoreThreadTimeOut(false);
475 setThreadFactory(this);
476 final Runnable shutdownHook = new Runnable() {
477 public void run() {
478 AccessController.doPrivileged(new PrivilegedAction<Void>() {
479 public Void run() {
480 shutdownNow();
481 return null;
482 }
483 });
484 }
485 };
486 AccessController.doPrivileged(new PrivilegedAction<Void>() {
487 public Void run() {
488 Runtime.getRuntime().addShutdownHook(
489 new Thread(shutdownHook)
490 );
491 return null;
492 }
493 });
494 }
495
496 public synchronized Thread newThread(final Runnable task) {
497 final Runnable comRun = new Runnable() {
498 public void run() {
499 try {
500 initializeCom();
501 task.run();
502 } finally {
503 uninitializeCom();
504 }
505 }
506 };
507 comThread = AccessController.doPrivileged((PrivilegedAction<Thread>) () -> {
508 /* The thread must be a member of a thread group
509 * which will not get GCed before VM exit.
510 * Make its parent the top-level thread group.
511 */
512 ThreadGroup rootTG = ThreadGroupUtils.getRootThreadGroup();
513 Thread thread = new Thread(rootTG, comRun, "Swing-Shell");
514 thread.setDaemon(true);
515 return thread;
516 }
517 );
518 return comThread;
519 }
520
521 public <T> T invoke(Callable<T> task) throws Exception {
522 if (Thread.currentThread() == comThread) {
523 // if it's already called from the COM
524 // thread, we don't need to delegate the task
525 return task.call();
526 } else {
527 final Future<T> future;
528
529 try {
530 future = submit(task);
531 } catch (RejectedExecutionException e) {
532 throw new InterruptedException(e.getMessage());
533 }
534
535 try {
536 return future.get();
537 } catch (InterruptedException e) {
538 AccessController.doPrivileged(new PrivilegedAction<Void>() {
539 public Void run() {
540 future.cancel(true);
541
542 return null;
543 }
544 });
545
546 throw e;
547 } catch (ExecutionException e) {
548 Throwable cause = e.getCause();
549
550 if (cause instanceof Exception) {
551 throw (Exception) cause;
552 }
553
554 if (cause instanceof Error) {
555 throw (Error) cause;
556 }
557
558 throw new RuntimeException("Unexpected error", cause);
559 }
560 }
561 }
562 }
563
564 static native void initializeCom();
565
566 static native void uninitializeCom();
567 }
--- EOF ---