package org.limewire.ui.swing.util;
import java.awt.Component;
import java.util.HashMap;
import java.util.Map;
import org.limewire.util.OSUtils;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;
import foxtrot.Job;
import foxtrot.Worker;
/**
* A modal Folder chooser dialog box. This uses native calls in JNA
* to load a native folder chooser on Windows systems. This class will not
* run on other platforms.
*/
public class WindowsFolderChooser {
//see http://msdn.microsoft.com/en-us/library/bb773205(VS.85).aspx
//for documentation on these flags and the BROWSEINFO struct
private final static int BIF_RETURNONLYFSDIRS = 1;
private final static int BIF_EDITBOX = 16;
private final static int BIF_NEWDIALOGSTYLE = 64;
//flags for events within the Folder Dialog
private static final int BFFM_INITIALIZED = 1;
private static final int BFFM_SETSELECTION = 1024 + 103;
/**
* Handle to the window shell.
*/
private Shell32 shell32;
/**
* Handle to the Structure that contains information about the Folder Chooser.
*/
private Shell32.BROWSEINFO info;
/**
* Creates a native Folder Chooser structure.
*
* @param msg the message to show the user.
* @param showEditBox true shows the edit box below the tree
* @param allowNewFolder true allows users to create a new folder in this dialog
* @param currentDirectory the folder to show selected
*/
public WindowsFolderChooser(Component component, String msg, boolean showEditBox, boolean allowNewFolder, final String currentDirectory){
// listens for callback events from changes within the Folder Chooser.
BrowseInfoCallback proc = new BrowseInfoCallback() {
/**
* When the Folder Chooser is initialized, expand and select the given
* directory.
*/
@Override
public int callback(Pointer wnd, int msg, int param, int lpData) {
if(msg == BFFM_INITIALIZED && currentDirectory != null) {
User32.INSTANCE.PostMessage(wnd, BFFM_SETSELECTION, 1, currentDirectory);
}
return 0;
}
};
shell32 = Shell32.INSTANCE;
// set visiblity flags for various components within the dialog
int flags = showEditBox ? BIF_EDITBOX : 0;
flags = flags | (allowNewFolder ? BIF_NEWDIALOGSTYLE : 0);
flags = flags | BIF_RETURNONLYFSDIRS;
// create the structure that holds the Folder Chooser information
info = new Shell32.BROWSEINFO();
info.lpszTitle = msg;
info.hwndOwner = Native.getComponentPointer(component);
info.lpfn = proc;
info.ulFlags = flags;
}
/**
* Makes the native Folder Chooser visible.
*
* @return a string containing the absolute path the user choose
*/
public String showWidget() {
String returnPath = (String)Worker.post(new Job() {
@Override
public Object run() {
byte[] path = new byte[OSUtils.getMaxPathLength()];
Pointer ptr = shell32.SHBrowseForFolder(info);
// get path selected by the user
shell32.SHGetPathFromIDList(ptr, path);
String returnPath = Native.toString(path);
// Dispose of the return path structure
Ole32.INSTANCE.CoTaskMemFree(ptr);
return returnPath;
}
});
return returnPath;
}
/**
* An Java interface for the Shell32.dll library.
*/
private interface Shell32 extends Library {
Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32", Shell32.class);
// A Java version of the C++ struct used for this dialog
// http://msdn.microsoft.com/en-us/library/bb773205(VS.85).aspx
class BROWSEINFO extends Structure {
@SuppressWarnings("unused") public Pointer hwndOwner;
@SuppressWarnings("unused") public Pointer pidlRoot;
@SuppressWarnings("unused") public Pointer pszDisplayName;
@SuppressWarnings("unused") public String lpszTitle;
@SuppressWarnings("unused") public int ulFlags;
@SuppressWarnings("unused") public BrowseInfoCallback lpfn;
@SuppressWarnings("unused") public Pointer lParam;
@SuppressWarnings("unused") public int iImage;
}
/**
* Displays a dialog box that enables the user to select a folder.
*/
Pointer SHBrowseForFolder(BROWSEINFO info);
/**
* Converts an item identifier list to a file system path.
*/
Boolean SHGetPathFromIDList(Pointer idl, byte[] path);
}
/**
* A Java Interface for the Ole32 dll library.
*/
private interface Ole32 extends Library {
Ole32 INSTANCE = (Ole32) Native.loadLibrary("ole32", Ole32.class);
/**
* Frees memory used by the given pointer
*/
void CoTaskMemFree(Pointer pointer);
}
/**
* A Java interface for User32 dll library.
*/
private interface User32 extends StdCallLibrary {
/** Standard options to use the unicode version of a w32 API. */
@SuppressWarnings("unchecked")
Map UNICODE_OPTIONS = new HashMap() {
{
put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
}
};
/** Standard options to use the ASCII/MBCS version of a w32 API. */
@SuppressWarnings("unchecked")
Map ASCII_OPTIONS = new HashMap() {
{
put(OPTION_TYPE_MAPPER, W32APITypeMapper.ASCII);
put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.ASCII);
}
};
Map DEFAULT_OPTIONS = Boolean.getBoolean("w32.ascii") ? ASCII_OPTIONS : UNICODE_OPTIONS;
User32 INSTANCE = (User32) Native.loadLibrary("user32", User32.class, DEFAULT_OPTIONS);
/**
* Posts a message within the message queue of the associated thread that
* created the given window and returns immediately.
*/
void PostMessage(Pointer pointer, int msg, int wParam, String lParam) ;
}
/**
* A callback function for receiving change events from a Dialog.
*/
private interface BrowseInfoCallback extends StdCallLibrary.StdCallCallback {
/**
* The dialog box calls this function when an event occurs
*/
int callback(Pointer hWnd , int uMsg, int lParam, int lpData);
}
}