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