Adding SwingJS interface and utility classes
[jalview.git] / src / javajs / async / AsyncDialog.java
1 package javajs.async;
2
3 import java.awt.Component;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.beans.PropertyChangeEvent;
7 import java.beans.PropertyChangeListener;
8
9 import javax.swing.Icon;
10 import javax.swing.JOptionPane;
11 import javax.swing.plaf.UIResource;
12
13 /**
14  * A class to manage asynchronous input, option, and confirmation dialogs.
15  * 
16  * @author Bob Hanson hansonr_at_stolaf.edu
17  *
18  */
19 public class AsyncDialog implements PropertyChangeListener {
20
21 // see discussion in net.sf.j2s.core/doc/Differences.txt
22 //
23 // Confirmation dialog example. Note moving the parent component into the constructor.
24 // Original:
25 //
26 //              private void promptQuit() {
27 //                      int sel = JOptionPane.showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION);
28 //                      switch (sel) {
29 //                      case JOptionPane.YES_OPTION:
30 //                              resultsTab.clean();
31 //                              seqs.dispose();
32 //                              if (fromMain) {
33 //                                      System.exit(0);
34 //                              }
35 //                              break;
36 //                      }
37 //              }
38 //
39 // revised: 
40 //
41 //              private void promptQuitAsync() {
42 //                      new AsyncDialog().showConfirmDialog(null, PROMPT_EXIT, NAME, JOptionPane.YES_NO_OPTION, new ActionListener() {
43 //
44 //      @Override
45 //      public void actionPerformed(ActionEvent e) {
46 //          int sel = ((AsyncDialog)e.getSource()).getOption();
47 //              switch (sel) {
48 //              case JOptionPane.YES_OPTION:
49 //                      resultsTab.clean();
50 //                      seqs.dispose();
51 //                      if (fromMain) {
52 //                              System.exit(0);
53 //                      }
54 //                      break;
55 //              }
56 //      }}));
57 //              }
58
59         
60         public AsyncDialog() {
61         }
62         
63         private ActionListener actionListener;
64         private Object choice;
65         private Object[] options;
66         private Object value;
67         private boolean wantsInput;
68
69         // These options can be supplemented as desired.
70
71         
72         /**
73          * Synchronous call; OK in JavaScript as long as we are using a JavaScript prompt() call
74          * 
75          * @param frame
76          * @param msg
77          * @return
78          */
79         @Deprecated
80         public static String showInputDialog(Component frame, String msg) {
81                 return JOptionPane.showInputDialog(frame, msg);
82         }
83
84         public void showInputDialog(Component frame, Object message, ActionListener a) {
85                 setListener(a);
86                 wantsInput = true;
87                 process(JOptionPane.showInputDialog(frame, message));
88                 unsetListener();
89         }
90
91         public void showInputDialog(Component frame, Object message, String title, int messageType, Icon icon,
92                         Object[] selectionValues, Object initialSelectionValue, ActionListener a) {
93                 setListener(a);
94                 wantsInput = true;
95                 process(JOptionPane.showInputDialog(frame, message, title, messageType, icon, selectionValues,
96                                 initialSelectionValue));
97                 unsetListener();
98         }
99
100         public void showMessageDialog(Component frame, Object message, ActionListener a) {
101                 setListener(a);
102                 JOptionPane.showMessageDialog(frame, message);
103                 unsetListener();
104                 if (/** @j2sNative false || */true)
105                         process("" + message);
106         }
107
108         public void showOptionDialog(Component frame, Object message, String title, int optionType, int messageType,
109                         Icon icon, Object[] options, Object initialValue, ActionListener a) {
110                 actionListener = a;
111                 this.options = options;
112                 setListener(a);
113                 process(JOptionPane.showOptionDialog(frame, message, title, optionType, messageType, icon, options,
114                                 initialValue));
115                 unsetListener();
116         }
117
118         public void showConfirmDialog(Component frame, Object message, String title, ActionListener a) {
119                 showConfirmDialog(frame, message, title, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, a);
120         }
121
122         public void showConfirmDialog(Component frame, Object message, String title, int optionType, ActionListener a) {
123                 showConfirmDialog(frame, message, title, optionType, JOptionPane.QUESTION_MESSAGE, a);
124         }
125
126         public void showConfirmDialog(Component frame, Object message, String title, int optionType, int messageType,
127                         ActionListener a) {
128                 setListener(a);
129                 process(JOptionPane.showConfirmDialog(frame, message, title, optionType, messageType));
130                 unsetListener();
131         }
132
133         /**
134          * retrieve selection from the ActionEvent, for which "this" is getSource()
135          * 
136          * @return
137          */
138         public Object getChoice() {
139                 return choice;
140         }
141
142         public int getOption() {
143                 if (!(choice instanceof Integer)) {
144                         throw new java.lang.IllegalArgumentException("AsyncDialog.getOption called for non-Integer choice");
145                 }
146                 return ((Integer) choice).intValue();
147         }
148
149         /**
150          * A dialog option that allows for YES, NO, and CLOSE options via
151          * ActionListener. ActionEvent.getID() contains the reply.
152          * 
153          * @param parent   The parent component for the dialog
154          * @param message  The text of the message to display
155          * @param title    Optional title defaults to "Question"
156          * @param listener Handle options based on an ActionEvent
157          */
158         public static void showYesNoAsync(Component parent, Object message, String title, ActionListener listener) {
159                 new AsyncDialog().showConfirmDialog(parent, message, (title == null ? "Question" : title),
160                                 JOptionPane.YES_NO_OPTION, listener);
161         }
162
163         /**
164          * A dialog option that involves just a YES follower.
165          * @param parent
166          * @param message
167          * @param title TODO
168          * @param yes
169          */
170         public static void showYesAsync(Component parent, Object message, String title, Runnable yes) {
171                 AsyncDialog.showYesNoAsync(parent, message, title, new ActionListener() {
172
173                         @Override
174                         public void actionPerformed(ActionEvent e) {
175                                 if (e.getID() == JOptionPane.YES_OPTION) {
176                                         yes.run();
177                                 }
178                         }
179                         
180                 });
181         }
182
183         /**
184          * A dialog option that involves just an OK follower.
185          * @param parent
186          * @param message
187          * @param title
188          * @param ok
189          */
190         public static void showOKAsync(Component parent, Object message, String title, Runnable ok) {
191                 new AsyncDialog().showConfirmDialog(parent, message, title, JOptionPane.OK_CANCEL_OPTION, new ActionListener() {
192
193                         @Override
194                         public void actionPerformed(ActionEvent e) {
195                                 if (e.getID() == JOptionPane.OK_OPTION) {
196                                         ok.run();
197                                 }
198                         }
199                         
200                 });
201         }
202
203
204         private void setListener(ActionListener a) {
205                 actionListener = a;
206                 @SuppressWarnings("unused")
207                 Class c = JOptionPane.class; // loads the class
208                 /** @j2sNative c.$clazz$.listener = this */
209         }
210
211         private void unsetListener() {
212                 /** @j2sNative javax.swing.JOptionPane.listener = null */
213         }
214
215         /**
216          * Switch from property change to action.
217          * 
218          */
219         @Override
220         public void propertyChange(PropertyChangeEvent evt) {
221                 value = evt.getNewValue();
222                 switch (evt.getPropertyName()) {
223                 case "inputValue":                      
224                         process(value);
225                         break;
226                 case "value":
227                         if (value != null && options == null && !(value instanceof Integer)) {
228                                 process(getOptionIndex(((JOptionPane) evt.getSource()).getOptions(), value));
229                                 return;
230                         }
231                         if (options != null) {
232                                 int i = getOptionIndex(options, value);
233                                 value = Integer.valueOf(i >= 0 ? i : JOptionPane.CLOSED_OPTION);
234                         } 
235                         process(value);
236                         break;
237                 }
238         }
239
240         private int getOptionIndex(Object[] options, Object val) {
241                 if (options != null)
242                         for (int i = 0; i < options.length; i++) {
243                                 if (options[i] == val)
244                                         return i;
245                         }
246                 return -1;
247         }
248
249         public Object getValue() {
250                 if (wantsInput || options == null)
251                         return value;
252                 int val = ((Integer) value).intValue();
253                 return (val < 0 ? null : options[val]);
254         }
255         
256         private boolean processed;
257
258         /**
259          * Return for confirm dialog.
260          * 
261          * @param ret may be JavaScript NaN, testable as ret != ret or ret != - -ret
262          */
263         private void process(int ret) {
264                 if (ret != -(-ret) || processed)
265                         return;
266                 processed = true;
267                 choice = ret;
268                 actionListener.actionPerformed(new ActionEvent(this, ret, "SelectedOption"));
269         }
270
271         private void process(Object ret) {
272                 if (ret instanceof UIResource || processed)
273                         return;
274                 processed = true;
275                 choice = ret;
276                 actionListener.actionPerformed(new ActionEvent(this, 
277                                 ret == null ? JOptionPane.CANCEL_OPTION : JOptionPane.OK_OPTION, 
278                                                 (ret == null ? null : ret.toString())));
279         }
280
281
282 }