0N/A/*
3261N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.awt.shell;
0N/A
1736N/Aimport java.awt.*;
1736N/Aimport java.awt.image.BufferedImage;
0N/A
0N/Aimport java.io.File;
0N/Aimport java.io.FileNotFoundException;
0N/Aimport java.io.IOException;
0N/Aimport java.security.AccessController;
1083N/Aimport java.security.PrivilegedAction;
0N/Aimport java.util.*;
1736N/Aimport java.util.List;
1083N/Aimport java.util.concurrent.*;
1083N/A
0N/Aimport sun.security.action.LoadLibraryAction;
0N/A
0N/Aimport static sun.awt.shell.Win32ShellFolder2.*;
0N/Aimport sun.awt.OSInfo;
0N/A
0N/A// NOTE: This class supersedes Win32ShellFolderManager, which was removed
0N/A// from distribution after version 1.4.2.
0N/A
0N/A/**
0N/A * @author Michael Martak
0N/A * @author Leif Samuelsson
0N/A * @author Kenneth Russell
0N/A * @since 1.4
0N/A */
0N/A
0N/Apublic class Win32ShellFolderManager2 extends ShellFolderManager {
0N/A
0N/A static {
0N/A // Load library here
0N/A AccessController.doPrivileged(new LoadLibraryAction("awt"));
0N/A }
0N/A
0N/A public ShellFolder createShellFolder(File file) throws FileNotFoundException {
1453N/A try {
1453N/A return createShellFolder(getDesktop(), file);
1453N/A } catch (InterruptedException e) {
1453N/A throw new FileNotFoundException("Execution was interrupted");
1453N/A }
0N/A }
0N/A
1453N/A static Win32ShellFolder2 createShellFolder(Win32ShellFolder2 parent, File file)
1453N/A throws FileNotFoundException, InterruptedException {
0N/A long pIDL;
0N/A try {
0N/A pIDL = parent.parseDisplayName(file.getCanonicalPath());
0N/A } catch (IOException ex) {
0N/A pIDL = 0;
0N/A }
0N/A if (pIDL == 0) {
0N/A // Shouldn't happen but watch for it anyway
0N/A throw new FileNotFoundException("File " + file.getAbsolutePath() + " not found");
0N/A }
1909N/A
1909N/A try {
1909N/A return createShellFolderFromRelativePIDL(parent, pIDL);
1909N/A } finally {
1909N/A Win32ShellFolder2.releasePIDL(pIDL);
1909N/A }
0N/A }
0N/A
1453N/A static Win32ShellFolder2 createShellFolderFromRelativePIDL(Win32ShellFolder2 parent, long pIDL)
1453N/A throws InterruptedException {
0N/A // Walk down this relative pIDL, creating new nodes for each of the entries
0N/A while (pIDL != 0) {
0N/A long curPIDL = Win32ShellFolder2.copyFirstPIDLEntry(pIDL);
0N/A if (curPIDL != 0) {
0N/A parent = new Win32ShellFolder2(parent, curPIDL);
0N/A pIDL = Win32ShellFolder2.getNextPIDLEntry(pIDL);
0N/A } else {
0N/A // The list is empty if the parent is Desktop and pIDL is a shortcut to Desktop
0N/A break;
0N/A }
0N/A }
0N/A return parent;
0N/A }
0N/A
1736N/A private static final int VIEW_LIST = 2;
1736N/A private static final int VIEW_DETAILS = 3;
1736N/A private static final int VIEW_PARENTFOLDER = 8;
1736N/A private static final int VIEW_NEWFOLDER = 11;
1736N/A
1736N/A private static final Image[] STANDARD_VIEW_BUTTONS = new Image[12];
1736N/A
1736N/A private static Image getStandardViewButton(int iconIndex) {
1736N/A Image result = STANDARD_VIEW_BUTTONS[iconIndex];
1736N/A
1736N/A if (result != null) {
1736N/A return result;
1736N/A }
1736N/A
1736N/A BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
1736N/A
1736N/A img.setRGB(0, 0, 16, 16, Win32ShellFolder2.getStandardViewButton0(iconIndex), 0, 16);
1736N/A
1736N/A STANDARD_VIEW_BUTTONS[iconIndex] = img;
1736N/A
1736N/A return img;
1736N/A }
1736N/A
0N/A // Special folders
0N/A private static Win32ShellFolder2 desktop;
0N/A private static Win32ShellFolder2 drives;
0N/A private static Win32ShellFolder2 recent;
0N/A private static Win32ShellFolder2 network;
0N/A private static Win32ShellFolder2 personal;
0N/A
0N/A static Win32ShellFolder2 getDesktop() {
0N/A if (desktop == null) {
0N/A try {
0N/A desktop = new Win32ShellFolder2(DESKTOP);
0N/A } catch (IOException e) {
1453N/A // Ignore error
1453N/A } catch (InterruptedException e) {
1453N/A // Ignore error
0N/A }
0N/A }
0N/A return desktop;
0N/A }
0N/A
0N/A static Win32ShellFolder2 getDrives() {
0N/A if (drives == null) {
0N/A try {
0N/A drives = new Win32ShellFolder2(DRIVES);
0N/A } catch (IOException e) {
1453N/A // Ignore error
1453N/A } catch (InterruptedException e) {
1453N/A // Ignore error
0N/A }
0N/A }
0N/A return drives;
0N/A }
0N/A
0N/A static Win32ShellFolder2 getRecent() {
0N/A if (recent == null) {
0N/A try {
0N/A String path = Win32ShellFolder2.getFileSystemPath(RECENT);
0N/A if (path != null) {
0N/A recent = createShellFolder(getDesktop(), new File(path));
0N/A }
1453N/A } catch (InterruptedException e) {
1453N/A // Ignore error
0N/A } catch (IOException e) {
1453N/A // Ignore error
0N/A }
0N/A }
0N/A return recent;
0N/A }
0N/A
0N/A static Win32ShellFolder2 getNetwork() {
0N/A if (network == null) {
0N/A try {
0N/A network = new Win32ShellFolder2(NETWORK);
0N/A } catch (IOException e) {
1453N/A // Ignore error
1453N/A } catch (InterruptedException e) {
1453N/A // Ignore error
0N/A }
0N/A }
0N/A return network;
0N/A }
0N/A
0N/A static Win32ShellFolder2 getPersonal() {
0N/A if (personal == null) {
0N/A try {
0N/A String path = Win32ShellFolder2.getFileSystemPath(PERSONAL);
0N/A if (path != null) {
0N/A Win32ShellFolder2 desktop = getDesktop();
0N/A personal = desktop.getChildByPath(path);
0N/A if (personal == null) {
0N/A personal = createShellFolder(getDesktop(), new File(path));
0N/A }
0N/A if (personal != null) {
0N/A personal.setIsPersonal();
0N/A }
0N/A }
1453N/A } catch (InterruptedException e) {
1453N/A // Ignore error
0N/A } catch (IOException e) {
1453N/A // Ignore error
0N/A }
0N/A }
0N/A return personal;
0N/A }
0N/A
0N/A
0N/A private static File[] roots;
0N/A
0N/A /**
0N/A * @param key a <code>String</code>
0N/A * "fileChooserDefaultFolder":
0N/A * Returns a <code>File</code> - the default shellfolder for a new filechooser
0N/A * "roots":
0N/A * Returns a <code>File[]</code> - containing the root(s) of the displayable hierarchy
0N/A * "fileChooserComboBoxFolders":
0N/A * Returns a <code>File[]</code> - an array of shellfolders representing the list to
0N/A * show by default in the file chooser's combobox
0N/A * "fileChooserShortcutPanelFolders":
0N/A * Returns a <code>File[]</code> - an array of shellfolders representing well-known
0N/A * folders, such as Desktop, Documents, History, Network, Home, etc.
0N/A * This is used in the shortcut panel of the filechooser on Windows 2000
0N/A * and Windows Me.
1736N/A * "fileChooserIcon <icon>":
1736N/A * Returns an <code>Image</code> - icon can be ListView, DetailsView, UpFolder, NewFolder or
1736N/A * ViewMenu (Windows only).
0N/A * "optionPaneIcon iconName":
0N/A * Returns an <code>Image</code> - icon from the system icon list
0N/A *
0N/A * @return An Object matching the key string.
0N/A */
0N/A public Object get(String key) {
0N/A if (key.equals("fileChooserDefaultFolder")) {
0N/A File file = getPersonal();
0N/A if (file == null) {
0N/A file = getDesktop();
0N/A }
0N/A return file;
0N/A } else if (key.equals("roots")) {
0N/A // Should be "History" and "Desktop" ?
0N/A if (roots == null) {
0N/A File desktop = getDesktop();
0N/A if (desktop != null) {
0N/A roots = new File[] { desktop };
0N/A } else {
0N/A roots = (File[])super.get(key);
0N/A }
0N/A }
0N/A return roots;
0N/A } else if (key.equals("fileChooserComboBoxFolders")) {
0N/A Win32ShellFolder2 desktop = getDesktop();
0N/A
0N/A if (desktop != null) {
0N/A ArrayList<File> folders = new ArrayList<File>();
0N/A Win32ShellFolder2 drives = getDrives();
0N/A
0N/A Win32ShellFolder2 recentFolder = getRecent();
0N/A if (recentFolder != null && OSInfo.getWindowsVersion().compareTo(OSInfo.WINDOWS_2000) >= 0) {
0N/A folders.add(recentFolder);
0N/A }
0N/A
0N/A folders.add(desktop);
0N/A // Add all second level folders
0N/A File[] secondLevelFolders = desktop.listFiles();
0N/A Arrays.sort(secondLevelFolders);
0N/A for (File secondLevelFolder : secondLevelFolders) {
0N/A Win32ShellFolder2 folder = (Win32ShellFolder2) secondLevelFolder;
1905N/A if (!folder.isFileSystem() || (folder.isDirectory() && !folder.isLink())) {
0N/A folders.add(folder);
0N/A // Add third level for "My Computer"
0N/A if (folder.equals(drives)) {
0N/A File[] thirdLevelFolders = folder.listFiles();
344N/A if (thirdLevelFolders != null && thirdLevelFolders.length > 0) {
344N/A List<File> thirdLevelFoldersList = Arrays.asList(thirdLevelFolders);
344N/A
344N/A folder.sortChildren(thirdLevelFoldersList);
344N/A folders.addAll(thirdLevelFoldersList);
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return folders.toArray(new File[folders.size()]);
0N/A } else {
0N/A return super.get(key);
0N/A }
0N/A } else if (key.equals("fileChooserShortcutPanelFolders")) {
0N/A Toolkit toolkit = Toolkit.getDefaultToolkit();
0N/A ArrayList<File> folders = new ArrayList<File>();
0N/A int i = 0;
0N/A Object value;
0N/A do {
0N/A value = toolkit.getDesktopProperty("win.comdlg.placesBarPlace" + i++);
0N/A try {
0N/A if (value instanceof Integer) {
0N/A // A CSIDL
0N/A folders.add(new Win32ShellFolder2((Integer)value));
0N/A } else if (value instanceof String) {
0N/A // A path
0N/A folders.add(createShellFolder(new File((String)value)));
0N/A }
0N/A } catch (IOException e) {
0N/A // Skip this value
1453N/A } catch (InterruptedException e) {
1453N/A // Return empty result
1453N/A return new File[0];
0N/A }
0N/A } while (value != null);
0N/A
0N/A if (folders.size() == 0) {
0N/A // Use default list of places
0N/A for (File f : new File[] {
0N/A getRecent(), getDesktop(), getPersonal(), getDrives(), getNetwork()
0N/A }) {
0N/A if (f != null) {
0N/A folders.add(f);
0N/A }
0N/A }
0N/A }
0N/A return folders.toArray(new File[folders.size()]);
0N/A } else if (key.startsWith("fileChooserIcon ")) {
1736N/A String name = key.substring(key.indexOf(" ") + 1);
1736N/A
1736N/A int iconIndex;
1736N/A
1736N/A if (name.equals("ListView") || name.equals("ViewMenu")) {
1736N/A iconIndex = VIEW_LIST;
1736N/A } else if (name.equals("DetailsView")) {
1736N/A iconIndex = VIEW_DETAILS;
1736N/A } else if (name.equals("UpFolder")) {
1736N/A iconIndex = VIEW_PARENTFOLDER;
1736N/A } else if (name.equals("NewFolder")) {
1736N/A iconIndex = VIEW_NEWFOLDER;
1736N/A } else {
1736N/A return null;
0N/A }
1736N/A
1736N/A return getStandardViewButton(iconIndex);
0N/A } else if (key.startsWith("optionPaneIcon ")) {
0N/A Win32ShellFolder2.SystemIcon iconType;
0N/A if (key == "optionPaneIcon Error") {
0N/A iconType = Win32ShellFolder2.SystemIcon.IDI_ERROR;
0N/A } else if (key == "optionPaneIcon Information") {
0N/A iconType = Win32ShellFolder2.SystemIcon.IDI_INFORMATION;
0N/A } else if (key == "optionPaneIcon Question") {
0N/A iconType = Win32ShellFolder2.SystemIcon.IDI_QUESTION;
0N/A } else if (key == "optionPaneIcon Warning") {
0N/A iconType = Win32ShellFolder2.SystemIcon.IDI_EXCLAMATION;
0N/A } else {
0N/A return null;
0N/A }
0N/A return Win32ShellFolder2.getSystemIcon(iconType);
231N/A } else if (key.startsWith("shell32Icon ") || key.startsWith("shell32LargeIcon ")) {
231N/A String name = key.substring(key.indexOf(" ") + 1);
0N/A try {
231N/A int i = Integer.parseInt(name);
0N/A if (i >= 0) {
231N/A return Win32ShellFolder2.getShell32Icon(i, key.startsWith("shell32LargeIcon "));
0N/A }
0N/A } catch (NumberFormatException ex) {
0N/A }
0N/A }
0N/A return null;
0N/A }
0N/A
0N/A /**
0N/A * Does <code>dir</code> represent a "computer" such as a node on the network, or
0N/A * "My Computer" on the desktop.
0N/A */
1560N/A public boolean isComputerNode(final File dir) {
0N/A if (dir != null && dir == getDrives()) {
0N/A return true;
0N/A } else {
1560N/A String path = AccessController.doPrivileged(new PrivilegedAction<String>() {
1560N/A public String run() {
1560N/A return dir.getAbsolutePath();
1560N/A }
1560N/A });
1560N/A
0N/A return (path.startsWith("\\\\") && path.indexOf("\\", 2) < 0); //Network path
0N/A }
0N/A }
0N/A
0N/A public boolean isFileSystemRoot(File dir) {
0N/A //Note: Removable drives don't "exist" but are listed in "My Computer"
0N/A if (dir != null) {
0N/A Win32ShellFolder2 drives = getDrives();
0N/A if (dir instanceof Win32ShellFolder2) {
0N/A Win32ShellFolder2 sf = (Win32ShellFolder2)dir;
0N/A if (sf.isFileSystem()) {
0N/A if (sf.parent != null) {
0N/A return sf.parent.equals(drives);
0N/A }
0N/A // else fall through ...
0N/A } else {
0N/A return false;
0N/A }
0N/A }
0N/A String path = dir.getPath();
2332N/A
2332N/A if (path.length() != 3 || path.charAt(1) != ':') {
2332N/A return false;
2332N/A }
2332N/A
2332N/A File[] files = drives.listFiles();
2332N/A
2332N/A return files != null && Arrays.asList(files).contains(dir);
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A private static List topFolderList = null;
0N/A static int compareShellFolders(Win32ShellFolder2 sf1, Win32ShellFolder2 sf2) {
0N/A boolean special1 = sf1.isSpecial();
0N/A boolean special2 = sf2.isSpecial();
0N/A
0N/A if (special1 || special2) {
0N/A if (topFolderList == null) {
0N/A ArrayList tmpTopFolderList = new ArrayList();
0N/A tmpTopFolderList.add(Win32ShellFolderManager2.getPersonal());
0N/A tmpTopFolderList.add(Win32ShellFolderManager2.getDesktop());
0N/A tmpTopFolderList.add(Win32ShellFolderManager2.getDrives());
0N/A tmpTopFolderList.add(Win32ShellFolderManager2.getNetwork());
0N/A topFolderList = tmpTopFolderList;
0N/A }
0N/A int i1 = topFolderList.indexOf(sf1);
0N/A int i2 = topFolderList.indexOf(sf2);
0N/A if (i1 >= 0 && i2 >= 0) {
0N/A return (i1 - i2);
0N/A } else if (i1 >= 0) {
0N/A return -1;
0N/A } else if (i2 >= 0) {
0N/A return 1;
0N/A }
0N/A }
0N/A
0N/A // Non-file shellfolders sort before files
0N/A if (special1 && !special2) {
0N/A return -1;
0N/A } else if (special2 && !special1) {
0N/A return 1;
0N/A }
0N/A
0N/A return compareNames(sf1.getAbsolutePath(), sf2.getAbsolutePath());
0N/A }
0N/A
0N/A static int compareNames(String name1, String name2) {
0N/A // First ignore case when comparing
344N/A int diff = name1.compareToIgnoreCase(name2);
0N/A if (diff != 0) {
0N/A return diff;
0N/A } else {
0N/A // May differ in case (e.g. "mail" vs. "Mail")
0N/A // We need this test for consistent sorting
0N/A return name1.compareTo(name2);
0N/A }
0N/A }
1083N/A
1083N/A @Override
1083N/A protected Invoker createInvoker() {
1083N/A return new ComInvoker();
1083N/A }
1083N/A
1083N/A private static class ComInvoker extends ThreadPoolExecutor implements ThreadFactory, ShellFolder.Invoker {
1083N/A private static Thread comThread;
1083N/A
1083N/A private ComInvoker() {
1083N/A super(1, 1, 0, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>());
1083N/A allowCoreThreadTimeOut(false);
1083N/A setThreadFactory(this);
1083N/A final Runnable shutdownHook = new Runnable() {
1083N/A public void run() {
1083N/A AccessController.doPrivileged(new PrivilegedAction<Void>() {
1083N/A public Void run() {
1083N/A shutdownNow();
1083N/A return null;
1083N/A }
1083N/A });
1083N/A }
1083N/A };
1083N/A AccessController.doPrivileged(new PrivilegedAction<Void>() {
1083N/A public Void run() {
1083N/A Runtime.getRuntime().addShutdownHook(
1083N/A new Thread(shutdownHook)
1083N/A );
1083N/A return null;
1083N/A }
1083N/A });
1083N/A }
1083N/A
1083N/A public synchronized Thread newThread(final Runnable task) {
1083N/A final Runnable comRun = new Runnable() {
1083N/A public void run() {
1083N/A try {
1083N/A initializeCom();
1083N/A task.run();
1083N/A } finally {
1083N/A uninitializeCom();
1083N/A }
1083N/A }
1083N/A };
1083N/A comThread =
1083N/A AccessController.doPrivileged(
1083N/A new PrivilegedAction<Thread>() {
1083N/A public Thread run() {
1083N/A /* The thread must be a member of a thread group
1083N/A * which will not get GCed before VM exit.
1083N/A * Make its parent the top-level thread group.
1083N/A */
1083N/A ThreadGroup tg = Thread.currentThread().getThreadGroup();
1083N/A for (ThreadGroup tgn = tg;
1083N/A tgn != null;
1083N/A tg = tgn, tgn = tg.getParent());
1083N/A Thread thread = new Thread(tg, comRun, "Swing-Shell");
1083N/A thread.setDaemon(true);
1083N/A return thread;
1083N/A }
1083N/A }
1083N/A );
1083N/A return comThread;
1083N/A }
1083N/A
1453N/A public <T> T invoke(Callable<T> task) throws Exception {
1453N/A if (Thread.currentThread() == comThread) {
1453N/A // if it's already called from the COM
1453N/A // thread, we don't need to delegate the task
1453N/A return task.call();
1453N/A } else {
1560N/A final Future<T> future;
1453N/A
1453N/A try {
1453N/A future = submit(task);
1453N/A } catch (RejectedExecutionException e) {
1453N/A throw new InterruptedException(e.getMessage());
1453N/A }
1231N/A
1453N/A try {
1453N/A return future.get();
1453N/A } catch (InterruptedException e) {
1560N/A AccessController.doPrivileged(new PrivilegedAction<Void>() {
1560N/A public Void run() {
1560N/A future.cancel(true);
1560N/A
1560N/A return null;
1560N/A }
1560N/A });
1453N/A
1453N/A throw e;
1453N/A } catch (ExecutionException e) {
1453N/A Throwable cause = e.getCause();
1453N/A
1453N/A if (cause instanceof Exception) {
1453N/A throw (Exception) cause;
1083N/A }
1453N/A
1453N/A if (cause instanceof Error) {
1453N/A throw (Error) cause;
1453N/A }
1453N/A
1453N/A throw new RuntimeException("Unexpected error", cause);
1083N/A }
1083N/A }
1083N/A }
1083N/A }
1083N/A
1083N/A static native void initializeCom();
1083N/A
1083N/A static native void uninitializeCom();
0N/A}