--- /dev/null
+package javajs.async;
+
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.function.Function;
+
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+import javax.swing.filechooser.FileSystemView;
+
+/**
+ * A simple Asynchronous file chooser for JavaScript and Java.
+ *
+ * Requires an OK runnable; JavaScript can return notification of cancel for
+ * file reading only, not saving.
+ *
+ * @author Bob Hanson
+ */
+
+public class AsyncFileChooser extends JFileChooser implements PropertyChangeListener {
+
+ private int optionSelected;
+ private Runnable ok, cancel; // sorry, no CANCEL in JavaScript for file open
+ private boolean isAsyncSave = true;
+ private static boolean notified;
+
+ public AsyncFileChooser() {
+ super();
+ }
+
+ public AsyncFileChooser(File file) {
+ super(file);
+ }
+
+ public AsyncFileChooser(File file, FileSystemView view) {
+ super(file, view);
+ }
+
+ @Deprecated
+ @Override
+ public int showDialog(Component frame, String btnText) {
+ // This one can come from JFileChooser - default is OPEN
+ return super.showDialog(frame, btnText);
+ }
+
+ private int err() {
+ try {
+ throw new java.lang.IllegalAccessException("Warning! AsyncFileChooser interface bypassed!");
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return JFileChooser.ERROR_OPTION;
+ }
+
+ @Deprecated
+ @Override
+ public int showOpenDialog(Component frame) {
+ return err();
+ }
+
+ @Override
+ public int showSaveDialog(Component frame) {
+ isAsyncSave = false;
+ return super.showSaveDialog(frame);
+ }
+
+ /**
+ *
+ * @param frame
+ * @param btnLabel "open" or "save"
+ * @param ok
+ * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
+ */
+ public void showDialog(Component frame, String btnLabel, Runnable ok, Runnable cancel) {
+ this.ok = ok;
+ if (getDialogType() != JFileChooser.SAVE_DIALOG && cancel != null)
+ notifyCancel();
+ process(super.showDialog(frame, btnLabel));
+ }
+
+ /**
+ *
+ * @param frame
+ * @param ok
+ * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
+ */
+ public void showOpenDialog(Component frame, Runnable ok, Runnable cancel) {
+ this.ok = ok;
+ if (cancel != null)
+ notifyCancel();
+ process(super.showOpenDialog(frame));
+ }
+
+ /**
+ *
+ * This just completes the set. It is not necessary for JavaScript, because JavaScript
+ * will just throw up a simple modal OK/Cancel message anyway.
+ *
+ * @param frame
+ * @param ok
+ * @param cancel must be null
+ */
+ public void showSaveDialog(Component frame, Runnable ok, Runnable cancel) {
+ this.ok = ok;
+ this.cancel = cancel;
+ process(super.showSaveDialog(frame));
+ }
+
+
+ /**
+ * Locate a file for input or output. Note that JavaScript will not return on cancel for OPEN_DIALOG.
+ *
+ * @param title The title for the dialog
+ * @param mode OPEN_DIALOG or SAVE_DIALOG
+ * @param processFile function to use when complete
+ */
+ public static void getFileAsync(Component parent, String title, int mode, Function<File, Void> processFile) {
+ // BH no references to this method. So changing its signature for asynchonous use
+ // And it didn't do as advertised - ran System.exit(0) if canceled
+ // create and display a file dialog
+ AsyncFileChooser fc = new AsyncFileChooser();
+ fc.setDialogTitle(title);
+ Runnable after = new Runnable() {
+
+ @Override
+ public void run() {
+ processFile.apply(fc.getSelectedFile());
+ }
+
+ };
+ if (mode == JFileChooser.OPEN_DIALOG) {
+ fc.showOpenDialog(parent, after, after);
+ } else {
+ fc.showSaveDialog(parent, after, after);
+ }
+
+ }
+
+ /**
+ * Run yes.run() if a file doesn't exist or if the user allows it, else run no.run()
+ * @param parent
+ * @param filename
+ * @param title
+ * @param yes (approved)
+ * @param no (optional)
+ */
+ public static void checkReplaceFileAsync(Component parent, File outfile, String title, Runnable yes, Runnable no) {
+ if (outfile.exists()) {
+ AsyncDialog.showYesNoAsync(parent,
+ outfile + " exists. Replace it?", null, new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ switch (e.getID()) {
+ case JOptionPane.YES_OPTION:
+ yes.run();
+ break;
+ default:
+ if (no != null)
+ no.run();
+ break;
+ }
+ }
+
+ });
+
+ } else {
+ yes.run();
+ }
+
+ }
+
+ private void notifyCancel() {
+ if (!notified) {
+ System.err.println("developer note: JavaScript cannot fire a FileChooser CANCEL action");
+ }
+ notified = true;
+ }
+
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ switch (evt.getPropertyName()) {
+ case "SelectedFile":
+ case "SelectedFiles":
+ process(optionSelected = (evt.getNewValue() == null ? CANCEL_OPTION : APPROVE_OPTION));
+ break;
+ }
+ }
+
+ private void process(int ret) {
+ if (ret != -(-ret))
+ return; // initial JavaScript return is NaN
+ optionSelected = ret;
+ File f = getSelectedFile();
+ if (f == null) {
+ if (cancel != null)
+ cancel.run();
+ } else {
+ if (ok != null)
+ ok.run();
+ }
+ }
+
+ public int getSelectedOption() {
+ return optionSelected;
+ }
+
+ public static byte[] getFileBytes(File f) {
+ return /** @j2sNative f.秘bytes || */null;
+ }
+
+}