swingjs/api, javajs/async
[jalview.git] / src / javajs / async / AsyncFileChooser.java
1 package javajs.async;
2
3
4 import java.awt.Component;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.beans.PropertyChangeEvent;
8 import java.beans.PropertyChangeListener;
9 import java.io.File;
10 import java.util.function.Function;
11
12 import javax.swing.JFileChooser;
13 import javax.swing.JOptionPane;
14 import javax.swing.filechooser.FileSystemView;
15
16 /**
17  * A simple Asynchronous file chooser for JavaScript and Java.
18  * 
19  * Requires an OK runnable; JavaScript can return notification of cancel for
20  * file reading only, not saving.
21  * 
22  * @author Bob Hanson
23  */
24
25 public class AsyncFileChooser extends JFileChooser implements PropertyChangeListener {
26
27         private int optionSelected;
28         private Runnable ok, cancel; // sorry, no CANCEL in JavaScript for file open
29         private boolean isAsyncSave = true;
30         private static boolean notified;
31
32         public AsyncFileChooser() {
33                 super();
34         }
35
36         public AsyncFileChooser(File file) {
37                 super(file);
38         }
39
40         public AsyncFileChooser(File file, FileSystemView view) {
41                 super(file, view);
42         }
43
44         @Deprecated
45         @Override
46         public int showDialog(Component frame, String btnText) {
47                 // This one can come from JFileChooser - default is OPEN
48                 return super.showDialog(frame, btnText);
49         }
50
51         private int err() {
52                 try {
53                         throw new java.lang.IllegalAccessException("Warning! AsyncFileChooser interface bypassed!");
54                 } catch (IllegalAccessException e) {
55                         e.printStackTrace();
56                 }
57                 return JFileChooser.ERROR_OPTION;
58         }
59
60         @Deprecated
61         @Override
62         public int showOpenDialog(Component frame) {
63                 return err();
64         }
65
66         @Override
67         public int showSaveDialog(Component frame) {
68                 isAsyncSave  = false;
69                 return super.showSaveDialog(frame);
70         }
71
72         /**
73          * 
74          * @param frame
75          * @param btnLabel "open" or "save"
76          * @param ok
77          * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
78          */
79         public void showDialog(Component frame, String btnLabel, Runnable ok, Runnable cancel) {
80                 this.ok = ok;
81                 if (getDialogType() != JFileChooser.SAVE_DIALOG && cancel != null)
82                         notifyCancel();
83                 process(super.showDialog(frame, btnLabel));
84         }
85
86         /**
87          * 
88          * @param frame
89          * @param ok
90          * @param cancel must be null; JavaScript cannot capture a cancel from a file dialog
91          */
92         public void showOpenDialog(Component frame, Runnable ok, Runnable cancel) {
93                 this.ok = ok;
94                 if (cancel != null)
95                         notifyCancel();
96                 process(super.showOpenDialog(frame));
97         }
98
99         /**
100          * 
101          * This just completes the set. It is not necessary for JavaScript, because JavaScript
102          * will just throw up a simple modal OK/Cancel message anyway.
103          * 
104          * @param frame
105          * @param ok
106          * @param cancel must be null
107          */
108         public void showSaveDialog(Component frame, Runnable ok, Runnable cancel) {
109                 this.ok = ok;
110                 this.cancel = cancel;
111                 process(super.showSaveDialog(frame));
112         }
113
114         
115         /**
116          * Locate a file for input or output. Note that JavaScript will not return on cancel for OPEN_DIALOG.
117          * 
118          * @param title       The title for the dialog
119          * @param mode        OPEN_DIALOG or SAVE_DIALOG
120          * @param processFile function to use when complete
121          */
122           public static void getFileAsync(Component parent, String title, int mode, Function<File, Void> processFile) {
123                   // BH no references to this method. So changing its signature for asynchonous use
124                   // And it didn't do as advertised - ran System.exit(0) if canceled
125             // create and display a file dialog
126                 AsyncFileChooser fc = new AsyncFileChooser();
127                 fc.setDialogTitle(title);
128                 Runnable after = new Runnable() {
129
130                         @Override
131                         public void run() {
132                                 processFile.apply(fc.getSelectedFile());
133                         }
134         
135                 };
136                 if (mode == JFileChooser.OPEN_DIALOG) {
137                         fc.showOpenDialog(parent, after, after);  
138                 } else {
139                         fc.showSaveDialog(parent, after, after);  
140                 }
141                                 
142           }
143             
144                 /**
145                  * Run yes.run() if a file doesn't exist or if the user allows it, else run no.run()
146                  * @param parent
147                  * @param filename
148                  * @param title
149                  * @param yes (approved)
150                  * @param no (optional)
151                  */
152                 public static void checkReplaceFileAsync(Component parent, File outfile, String title, Runnable yes, Runnable no) {
153                         if (outfile.exists()) {
154                                 AsyncDialog.showYesNoAsync(parent,
155                                                 outfile + " exists. Replace it?", null, new ActionListener() {
156                 
157                                                         @Override
158                                                         public void actionPerformed(ActionEvent e) {
159                                                                 switch (e.getID()) {
160                                                                 case JOptionPane.YES_OPTION:
161                                                                         yes.run();
162                                                                         break;
163                                                                 default:
164                                                                         if (no != null)
165                                                                                 no.run();
166                                                                         break;
167                                                                 }
168                                                         }
169                 
170                                                 });
171                 
172                         } else {
173                                 yes.run();
174                         }
175                 
176                 }
177
178         private void notifyCancel() {
179                 if (!notified) {
180                         System.err.println("developer note: JavaScript cannot fire a FileChooser CANCEL action");
181                 }
182                 notified = true;
183         }
184
185         @Override
186         public void propertyChange(PropertyChangeEvent evt) {
187                 switch (evt.getPropertyName()) {
188                 case "SelectedFile":
189                 case "SelectedFiles":
190                         process(optionSelected = (evt.getNewValue() == null ? CANCEL_OPTION : APPROVE_OPTION));
191                         break;
192                 }
193         }
194
195         private void process(int ret) {
196                 if (ret != -(-ret))
197                         return; // initial JavaScript return is NaN
198                 optionSelected = ret;
199                 File f = getSelectedFile();
200                 if (f == null) {
201                         if (cancel != null)
202                                 cancel.run();
203                 } else {
204                         if (ok != null)
205                                 ok.run();
206                 }
207         }
208
209         public int getSelectedOption() {
210                 return optionSelected;
211         }
212
213         public static byte[] getFileBytes(File f) {
214                 return /** @j2sNative f.秘bytes || */null;
215         }
216
217 }