2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.awt.AWTEvent;
24 import java.awt.ActiveEvent;
25 import java.awt.Component;
26 import java.awt.Container;
27 import java.awt.Dialog.ModalityType;
28 import java.awt.EventQueue;
29 import java.awt.HeadlessException;
30 import java.awt.MenuComponent;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.MouseAdapter;
36 import java.awt.event.MouseMotionAdapter;
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.List;
44 import java.util.concurrent.Callable;
45 import java.util.concurrent.Executors;
47 import javax.swing.Icon;
48 import javax.swing.JButton;
49 import javax.swing.JDialog;
50 import javax.swing.JFrame;
51 import javax.swing.JInternalFrame;
52 import javax.swing.JLayeredPane;
53 import javax.swing.JOptionPane;
54 import javax.swing.JPanel;
55 import javax.swing.SwingUtilities;
56 import javax.swing.UIManager;
57 import javax.swing.event.InternalFrameEvent;
58 import javax.swing.event.InternalFrameListener;
60 import jalview.util.Platform;
61 import jalview.util.dialogrunner.DialogRunnerI;
63 public class JvOptionPane extends JOptionPane
64 implements DialogRunnerI, PropertyChangeListener
66 private static final long serialVersionUID = -3019167117756785229L;
68 private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
70 private static boolean interactiveMode = true;
72 private Component parentComponent;
74 private Map<Object, Callable<Void>> callbacks = new HashMap<>();
77 * JalviewJS reports user choice in the dialog as the selected option (text);
78 * this list allows conversion to index (int)
80 List<Object> ourOptions;
82 public JvOptionPane(final Component parent)
84 this.parentComponent = Platform.isJS() ? this : parent;
88 public static int showConfirmDialog(Component parentComponent,
89 Object message) throws HeadlessException
91 // only called by test
92 return isInteractiveMode()
93 ? JOptionPane.showConfirmDialog(parentComponent, message)
94 : (int) getMockResponse();
98 * Message, title, optionType
100 * @param parentComponent
105 * @throws HeadlessException
107 public static int showConfirmDialog(Component parentComponent,
108 Object message, String title, int optionType)
109 throws HeadlessException
111 if (!isInteractiveMode())
113 return (int) getMockResponse();
117 case JvOptionPane.YES_NO_CANCEL_OPTION:
118 // FeatureRenderer amendFeatures ?? TODO ??
123 case JvOptionPane.YES_NO_OPTION:
124 // PromptUserConfig usage stats
125 // for now treated as "OK CANCEL"
127 case JvOptionPane.OK_CANCEL_OPTION:
128 // will fall back to simple HTML
129 return JOptionPane.showConfirmDialog(parentComponent, message, title,
135 * Adds a message type. Fallback is to just add it in the beginning.
137 * @param parentComponent
143 * @throws HeadlessException
145 public static int showConfirmDialog(Component parentComponent,
146 Object message, String title, int optionType, int messageType)
147 throws HeadlessException
149 // JalviewServicesChanged
150 // PromptUserConfig raiseDialog
151 return isInteractiveMode()
152 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
153 optionType, messageType)
154 : (int) getMockResponse();
160 * @param parentComponent
167 * @throws HeadlessException
169 public static int showConfirmDialog(Component parentComponent,
170 Object message, String title, int optionType, int messageType,
171 Icon icon) throws HeadlessException
173 // JvOptionPaneTest only
174 return isInteractiveMode()
175 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
176 optionType, messageType, icon)
177 : (int) getMockResponse();
181 * Internal version "OK"
183 * @param parentComponent
187 public static int showInternalConfirmDialog(Component parentComponent,
190 // JvOptionPaneTest only;
191 return isInteractiveMode()
192 ? JOptionPane.showInternalConfirmDialog(parentComponent,
194 : (int) getMockResponse();
198 * Internal version -- changed to standard version for now
200 * @param parentComponent
206 public static int showInternalConfirmDialog(Component parentComponent,
207 String message, String title, int optionType)
209 if (!isInteractiveMode())
211 return (int) getMockResponse();
215 case JvOptionPane.YES_NO_CANCEL_OPTION:
216 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
217 case JvOptionPane.YES_NO_OPTION:
218 // UserDefinedColoursSave -- relevant? TODO
221 case JvOptionPane.OK_CANCEL_OPTION:
223 // EditNameDialog --- uses panel for messsage TODO
225 // Desktop.inputURLMenuItem
227 return JOptionPane.showConfirmDialog(parentComponent, message, title,
234 * @param parentComponent
241 public static int showInternalConfirmDialog(Component parentComponent,
242 Object message, String title, int optionType, int messageType)
244 if (!isInteractiveMode())
246 return (int) getMockResponse();
250 case JvOptionPane.YES_NO_CANCEL_OPTION:
251 case JvOptionPane.YES_NO_OPTION:
252 // UserQuestionanaireCheck
256 case JvOptionPane.OK_CANCEL_OPTION:
257 // will fall back to simple HTML
258 return JOptionPane.showConfirmDialog(parentComponent, message, title,
259 optionType, messageType);
264 * adds icon; no longer internal
266 * @param parentComponent
274 public static int showInternalConfirmDialog(Component parentComponent,
275 Object message, String title, int optionType, int messageType,
278 if (!isInteractiveMode())
280 return (int) getMockResponse();
284 case JvOptionPane.YES_NO_CANCEL_OPTION:
285 case JvOptionPane.YES_NO_OPTION:
288 case JvOptionPane.OK_CANCEL_OPTION:
289 // Preferences editLink/newLink
290 return JOptionPane.showConfirmDialog(parentComponent, message, title,
291 optionType, messageType, icon);
297 * custom options full-featured
299 * @param parentComponent
306 * @param initialValue
308 * @throws HeadlessException
310 public static int showOptionDialog(Component parentComponent,
311 String message, String title, int optionType, int messageType,
312 Icon icon, Object[] options, Object initialValue)
313 throws HeadlessException
315 if (!isInteractiveMode())
317 return (int) getMockResponse();
323 // 1) AlignViewport for openLinkedAlignment
325 // Show a dialog with the option to open and link (cDNA <-> protein) as a
327 // alignment, either as a standalone alignment or in a split frame. Returns
328 // true if the new alignment was opened, false if not, because the user
329 // declined the offer.
331 // 2) UserDefinedColors warning about saving over a name already defined
333 return JOptionPane.showOptionDialog(parentComponent, message, title,
334 optionType, messageType, icon, options, initialValue);
341 * @throws HeadlessException
343 public static void showMessageDialog(Component parentComponent,
344 String message) throws HeadlessException
346 if (!isInteractiveMode())
348 outputMessage(message);
354 JOptionPane.showMessageDialog(parentComponent, message);
358 * OK with message, title, and type
360 * @param parentComponent
364 * @throws HeadlessException
366 public static void showMessageDialog(Component parentComponent,
367 String message, String title, int messageType)
368 throws HeadlessException
370 // 30 implementations -- all just fine.
372 if (!isInteractiveMode())
374 outputMessage(message);
378 JOptionPane.showMessageDialog(parentComponent,
379 getPrefix(messageType) + message, title, messageType);
383 * adds title and icon
385 * @param parentComponent
390 * @throws HeadlessException
392 public static void showMessageDialog(Component parentComponent,
393 String message, String title, int messageType, Icon icon)
394 throws HeadlessException
399 if (!isInteractiveMode())
401 outputMessage(message);
405 JOptionPane.showMessageDialog(parentComponent, message, title,
413 public static void showInternalMessageDialog(Component parentComponent,
417 // WsPreferences only
419 if (!isInteractiveMode())
421 outputMessage(message);
425 JOptionPane.showMessageDialog(parentComponent, message);
429 * Adds title and messageType
431 * @param parentComponent
436 public static void showInternalMessageDialog(Component parentComponent,
437 String message, String title, int messageType)
442 if (!isInteractiveMode())
444 outputMessage(message);
448 JOptionPane.showMessageDialog(parentComponent,
449 getPrefix(messageType) + message, title, messageType);
454 * @param parentComponent
460 public static void showInternalMessageDialog(Component parentComponent,
461 Object message, String title, int messageType, Icon icon)
466 if (!isInteractiveMode())
468 outputMessage(message);
472 JOptionPane.showMessageDialog(parentComponent, message, title,
480 * @throws HeadlessException
482 public static String showInputDialog(Object message)
483 throws HeadlessException
487 if (!isInteractiveMode())
489 return getMockResponse().toString();
492 return JOptionPane.showInputDialog(message);
496 * adds inital selection value
499 * @param initialSelectionValue
502 public static String showInputDialog(String message,
503 String initialSelectionValue)
505 if (!isInteractiveMode())
507 return getMockResponse().toString();
510 // AnnotationPanel character option
512 return JOptionPane.showInputDialog(message, initialSelectionValue);
516 * adds inital selection value
519 * @param initialSelectionValue
522 public static String showInputDialog(Object message,
523 Object initialSelectionValue)
525 if (!isInteractiveMode())
527 return getMockResponse().toString();
530 // AnnotationPanel character option
532 return JOptionPane.showInputDialog(message, initialSelectionValue);
538 * @param parentComponent
541 * @throws HeadlessException
543 public static String showInputDialog(Component parentComponent,
544 String message) throws HeadlessException
548 return isInteractiveMode()
549 ? JOptionPane.showInputDialog(parentComponent, message)
550 : getMockResponse().toString();
554 * input with initial selection
556 * @param parentComponent
558 * @param initialSelectionValue
561 public static String showInputDialog(Component parentComponent,
562 String message, String initialSelectionValue)
567 return isInteractiveMode()
568 ? JOptionPane.showInputDialog(parentComponent, message,
569 initialSelectionValue)
570 : getMockResponse().toString();
574 * input with initial selection
576 * @param parentComponent
578 * @param initialSelectionValue
581 public static String showInputDialog(Component parentComponent,
582 Object message, Object initialSelectionValue)
587 return isInteractiveMode()
588 ? JOptionPane.showInputDialog(parentComponent, message,
589 initialSelectionValue)
590 : getMockResponse().toString();
595 * @param parentComponent
600 * @throws HeadlessException
602 public static String showInputDialog(Component parentComponent,
603 String message, String title, int messageType)
604 throws HeadlessException
609 return isInteractiveMode()
610 ? JOptionPane.showInputDialog(parentComponent, message, title,
612 : getMockResponse().toString();
616 * Customized input option
618 * @param parentComponent
623 * @param selectionValues
624 * @param initialSelectionValue
626 * @throws HeadlessException
628 public static Object showInputDialog(Component parentComponent,
629 Object message, String title, int messageType, Icon icon,
630 Object[] selectionValues, Object initialSelectionValue)
631 throws HeadlessException
636 return isInteractiveMode()
637 ? JOptionPane.showInputDialog(parentComponent, message, title,
638 messageType, icon, selectionValues,
639 initialSelectionValue)
640 : getMockResponse().toString();
646 * @param parentComponent
650 public static String showInternalInputDialog(Component parentComponent,
655 return isInteractiveMode()
656 ? JOptionPane.showInternalInputDialog(parentComponent, message)
657 : getMockResponse().toString();
661 * internal with title and messageType
663 * @param parentComponent
669 public static String showInternalInputDialog(Component parentComponent,
670 String message, String title, int messageType)
673 // AlignFrame tabbedPane_mousePressed
675 return isInteractiveMode()
676 ? JOptionPane.showInternalInputDialog(parentComponent,
677 getPrefix(messageType) + message, title, messageType)
678 : getMockResponse().toString();
682 * customized internal
684 * @param parentComponent
689 * @param selectionValues
690 * @param initialSelectionValue
693 public static Object showInternalInputDialog(Component parentComponent,
694 String message, String title, int messageType, Icon icon,
695 Object[] selectionValues, Object initialSelectionValue)
699 return isInteractiveMode()
700 ? JOptionPane.showInternalInputDialog(parentComponent, message,
701 title, messageType, icon, selectionValues,
702 initialSelectionValue)
703 : getMockResponse().toString();
706 ///////////// end of options ///////////////
708 private static void outputMessage(Object message)
710 System.out.println(">>> JOption Message : " + message.toString());
713 public static Object getMockResponse()
718 public static void setMockResponse(Object mockOption)
720 JvOptionPane.mockResponse = mockOption;
723 public static void resetMock()
725 setMockResponse(JvOptionPane.CANCEL_OPTION);
726 setInteractiveMode(true);
729 public static boolean isInteractiveMode()
731 return interactiveMode;
734 public static void setInteractiveMode(boolean interactive)
736 JvOptionPane.interactiveMode = interactive;
739 private static String getPrefix(int messageType)
748 case JvOptionPane.WARNING_MESSAGE:
749 prefix = "WARNING! ";
751 case JvOptionPane.ERROR_MESSAGE:
762 * create a new option dialog that can be used to register responses - along
763 * lines of showOptionDialog
768 * @param defaultOption
769 * @param plainMessage
775 public static JvOptionPane newOptionDialog()
777 return new JvOptionPane(null);
780 public static JvOptionPane newOptionDialog(Component parentComponent)
782 return new JvOptionPane(parentComponent);
785 public void showDialog(String message, String title, int optionType,
786 int messageType, Icon icon, Object[] options, Object initialValue)
788 showDialog(message, title, optionType, messageType, icon, options,
792 public void showDialog(Object message, String title, int optionType,
793 int messageType, Icon icon, Object[] options, Object initialValue,
796 showDialog(message, title, optionType, messageType, icon, options,
797 initialValue, modal, null);
800 public void showDialog(Object message, String title, int optionType,
801 int messageType, Icon icon, Object[] options, Object initialValue,
802 boolean modal, JButton[] buttons)
804 if (!isInteractiveMode())
806 handleResponse(getMockResponse());
813 // 1) AlignViewport for openLinkedAlignment
815 // Show a dialog with the option to open and link (cDNA <-> protein) as a
817 // alignment, either as a standalone alignment or in a split frame. Returns
818 // true if the new alignment was opened, false if not, because the user
819 // declined the offer.
821 // 2) UserDefinedColors warning about saving over a name already defined
824 ourOptions = Arrays.asList(options);
828 boolean useButtons = false;
829 Object initialValueButton = null;
830 NOTNULL: if (buttons != null)
832 if (buttons.length != options.length)
834 jalview.bin.Console.error(
835 "Supplied buttons array not the same length as supplied options array.");
838 int[] buttonActions = { JOptionPane.YES_OPTION,
839 JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
840 for (int i = 0; i < options.length; i++)
842 Object o = options[i];
843 jalview.bin.Console.debug(
844 "Setting button " + i + " to '" + o.toString() + "'");
845 JButton jb = buttons[i];
847 if (o.equals(initialValue))
848 initialValueButton = jb;
850 int buttonAction = buttonActions[i];
851 Callable<Void> action = callbacks.get(buttonAction);
852 jb.setText((String) o);
853 jb.addActionListener(new ActionListener()
856 public void actionPerformed(ActionEvent e)
859 Object obj = e.getSource();
860 if (obj == null || !(obj instanceof Component))
862 jalview.bin.Console.debug(
863 "Could not find Component source of event object "
867 Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
868 JOptionPane.class, (Component) obj);
869 if (joptionpaneObject == null
870 || !(joptionpaneObject instanceof JOptionPane))
872 jalview.bin.Console.debug(
873 "Could not find JOptionPane ancestor of event object "
877 JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
878 joptionpane.setValue(buttonAction);
880 Executors.newSingleThreadExecutor().submit(action);
881 joptionpane.transferFocusBackward();
882 joptionpane.setVisible(false);
883 // put focus and raise parent window if possible, unless cancel or
885 boolean raiseParent = (parentComponent != null);
886 if (buttonAction == JOptionPane.CANCEL_OPTION)
888 if (optionType == JOptionPane.YES_NO_OPTION
889 && buttonAction == JOptionPane.NO_OPTION)
893 parentComponent.requestFocus();
894 if (parentComponent instanceof JInternalFrame)
896 JInternalFrame jif = (JInternalFrame) parentComponent;
901 else if (parentComponent instanceof Window)
903 Window w = (Window) parentComponent;
908 joptionpane.setVisible(false);
915 // use a JOptionPane as usual
916 int response = JOptionPane.showOptionDialog(parentComponent, message,
917 title, optionType, messageType, icon,
918 useButtons ? buttons : options,
919 useButtons ? initialValueButton : initialValue);
922 * In Java, the response is returned to this thread and handled here; (for
923 * Javascript, see propertyChange)
925 if (!Platform.isJS())
932 handleResponse(response);
938 * This is java similar to the swingjs handling, with the callbacks attached to
939 * the button press of the dialog. This means we can use a non-modal JDialog for
940 * the confirmation without blocking the GUI.
942 JOptionPane joptionpane = new JOptionPane();
943 // Make button options
944 int[] buttonActions = { JvOptionPane.YES_OPTION,
945 JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
947 // we need the strings to make the buttons with actionEventListener
950 ArrayList<String> options_default = new ArrayList<>();
952 .add(UIManager.getString("OptionPane.yesButtonText"));
953 if (optionType == JvOptionPane.YES_NO_OPTION
954 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
957 .add(UIManager.getString("OptionPane.noButtonText"));
959 if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
962 .add(UIManager.getString("OptionPane.cancelButtonText"));
964 options = options_default.toArray();
967 ArrayList<JButton> options_btns = new ArrayList<>();
968 Object initialValue_btn = null;
969 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
972 for (int i = 0; i < options.length && i < 3; i++)
974 Object o = options[i];
975 int buttonAction = buttonActions[i];
976 Callable<Void> action = callbacks.get(buttonAction);
977 JButton jb = new JButton();
978 jb.setText((String) o);
979 jb.addActionListener(new ActionListener()
982 public void actionPerformed(ActionEvent e)
984 joptionpane.setValue(buttonAction);
986 Executors.newSingleThreadExecutor().submit(action);
987 // joptionpane.transferFocusBackward();
988 joptionpane.transferFocusBackward();
989 joptionpane.setVisible(false);
990 // put focus and raise parent window if possible, unless cancel
992 boolean raiseParent = (parentComponent != null);
993 if (buttonAction == JvOptionPane.CANCEL_OPTION)
995 if (optionType == JvOptionPane.YES_NO_OPTION
996 && buttonAction == JvOptionPane.NO_OPTION)
1000 parentComponent.requestFocus();
1001 if (parentComponent instanceof JInternalFrame)
1003 JInternalFrame jif = (JInternalFrame) parentComponent;
1008 else if (parentComponent instanceof Window)
1010 Window w = (Window) parentComponent;
1015 joptionpane.setVisible(false);
1018 options_btns.add(jb);
1019 if (o.equals(initialValue))
1020 initialValue_btn = jb;
1023 joptionpane.setMessage(message);
1024 joptionpane.setMessageType(messageType);
1025 joptionpane.setOptionType(optionType);
1026 joptionpane.setIcon(icon);
1027 joptionpane.setOptions(
1028 Platform.isJS() ? options : options_btns.toArray());
1029 joptionpane.setInitialValue(
1030 Platform.isJS() ? initialValue : initialValue_btn);
1032 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1033 dialog.setIconImage(null);
1034 dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1035 : ModalityType.MODELESS);
1036 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1037 dialog.setVisible(true);
1041 public void showInternalDialog(JPanel mainPanel, String title,
1042 int yesNoCancelOption, int questionMessage, Icon icon,
1043 Object[] options, String initresponse)
1045 if (!isInteractiveMode())
1047 handleResponse(getMockResponse());
1050 // need to set these separately so we can set the title bar icon later
1051 this.setOptionType(yesNoCancelOption);
1052 this.setMessageType(questionMessage);
1054 this.setInitialValue(initresponse);
1055 this.setOptions(options);
1056 this.setMessage(mainPanel);
1058 ourOptions = Arrays.asList(options);
1060 if (parentComponent != this)
1062 JInternalFrame jif = this.createInternalFrame(parentComponent, title);
1063 jif.setFrameIcon(null);
1064 jif.addInternalFrameListener(new InternalFrameListener()
1067 public void internalFrameActivated(InternalFrameEvent arg0)
1072 public void internalFrameClosed(InternalFrameEvent arg0)
1074 JvOptionPane.this.internalDialogHandleResponse();
1078 public void internalFrameClosing(InternalFrameEvent arg0)
1083 public void internalFrameDeactivated(InternalFrameEvent arg0)
1088 public void internalFrameDeiconified(InternalFrameEvent arg0)
1093 public void internalFrameIconified(InternalFrameEvent arg0)
1098 public void internalFrameOpened(InternalFrameEvent arg0)
1102 jif.setVisible(true);
1108 JDialog dialog = this.createDialog(parentComponent, title);
1109 dialog.setIconImage(null);
1110 dialog.setVisible(true); // blocking
1111 this.internalDialogHandleResponse();
1116 private void internalDialogHandleResponse()
1118 String responseString = (String) this.getValue();
1119 int response = ourOptions.indexOf(responseString);
1121 if (!Platform.isJS())
1128 handleResponse(response);
1133 * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1134 * action) { callbacks.put(response, new Callable<Void>() {
1136 * @Override public Void call() { action.run(); return null; } }); return this;
1140 public JvOptionPane setResponseHandler(Object response,
1141 Callable<Void> action)
1143 callbacks.put(response, action);
1148 * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1151 public static int showDialogOnTop(String label, String actionString,
1152 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1154 if (!isInteractiveMode())
1156 return (int) getMockResponse();
1158 // Ensure Jalview window is brought to front (primarily for Quit
1159 // confirmation window to be visible)
1161 // This method of raising the Jalview window is broken in java
1162 // jalviewDesktop.setVisible(true);
1163 // jalviewDesktop.toFront();
1165 // A better hack which works is to create a new JFrame parent with
1166 // setAlwaysOnTop(true)
1167 JFrame dialogParent = new JFrame();
1168 dialogParent.setIconImage(null);
1169 dialogParent.setAlwaysOnTop(true);
1171 int answer = JOptionPane.showConfirmDialog(dialogParent, label,
1172 actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
1174 dialogParent.setAlwaysOnTop(false);
1175 dialogParent.dispose();
1180 public void showDialogOnTopAsync(String label, String actionString,
1181 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1182 Object[] options, Object initialValue, boolean modal)
1184 JFrame frame = new JFrame();
1185 frame.setIconImage(null);
1186 showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1187 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1190 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1191 String actionString, int JOPTIONPANE_OPTION,
1192 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1193 Object initialValue, boolean modal)
1195 showDialogOnTopAsync(dialogParent, label, actionString,
1196 JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1197 initialValue, modal, null);
1200 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1201 String actionString, int JOPTIONPANE_OPTION,
1202 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1203 Object initialValue, boolean modal, JButton[] buttons)
1205 if (!isInteractiveMode())
1207 handleResponse(getMockResponse());
1210 // Ensure Jalview window is brought to front (primarily for Quit
1211 // confirmation window to be visible)
1213 // This method of raising the Jalview window is broken in java
1214 // jalviewDesktop.setVisible(true);
1215 // jalviewDesktop.toFront();
1217 // A better hack which works is to create a new JFrame parent with
1218 // setAlwaysOnTop(true)
1219 dialogParent.setAlwaysOnTop(true);
1220 parentComponent = dialogParent;
1222 showDialog(label, actionString, JOPTIONPANE_OPTION,
1223 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1226 dialogParent.setAlwaysOnTop(false);
1227 dialogParent.dispose();
1231 * JalviewJS signals option selection by a property change event for the
1232 * option e.g. "OK". This methods responds to that by running the response
1233 * action that corresponds to that option.
1238 public void propertyChange(PropertyChangeEvent evt)
1240 Object newValue = evt.getNewValue();
1241 int ourOption = ourOptions.indexOf(newValue);
1244 handleResponse(ourOption);
1249 handleResponse(newValue);
1254 public void handleResponse(Object response)
1257 * this test is for NaN in Chrome
1259 if (response != null && !response.equals(response))
1263 Callable<Void> action = callbacks.get(response);
1269 } catch (Exception e)
1271 e.printStackTrace();
1273 if (parentComponent != null)
1274 parentComponent.requestFocus();
1279 * Create a non-modal confirm dialog
1281 public JDialog createDialog(Component parentComponent, Object message,
1282 String title, int optionType, int messageType, Icon icon,
1283 Object[] options, Object initialValue, boolean modal)
1285 return createDialog(parentComponent, message, title, optionType,
1286 messageType, icon, options, initialValue, modal, null);
1289 public JDialog createDialog(Component parentComponent, Object message,
1290 String title, int optionType, int messageType, Icon icon,
1291 Object[] options, Object initialValue, boolean modal,
1294 if (!isInteractiveMode())
1296 handleResponse(getMockResponse());
1299 JButton[] optionsButtons = null;
1300 Object initialValueButton = null;
1301 JOptionPane joptionpane = new JOptionPane();
1302 // Make button options
1303 int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1304 JOptionPane.CANCEL_OPTION };
1306 // we need the strings to make the buttons with actionEventListener
1307 if (options == null)
1309 ArrayList<String> options_default = new ArrayList<>();
1310 options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1311 if (optionType == JOptionPane.YES_NO_OPTION
1312 || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1314 options_default.add(UIManager.getString("OptionPane.noButtonText"));
1316 if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1319 .add(UIManager.getString("OptionPane.cancelButtonText"));
1321 options = options_default.toArray();
1323 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1326 if (((optionType == JOptionPane.YES_OPTION
1327 || optionType == JOptionPane.NO_OPTION
1328 || optionType == JOptionPane.CANCEL_OPTION
1329 || optionType == JOptionPane.OK_OPTION
1330 || optionType == JOptionPane.DEFAULT_OPTION)
1331 && options.length < 1)
1332 || ((optionType == JOptionPane.YES_NO_OPTION
1333 || optionType == JOptionPane.OK_CANCEL_OPTION)
1334 && options.length < 2)
1335 || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1336 && options.length < 3))
1339 .debug("JvOptionPane: not enough options for dialog type");
1341 optionsButtons = new JButton[options.length];
1342 for (int i = 0; i < options.length && i < 3; i++)
1344 Object o = options[i];
1345 int buttonAction = buttonActions[i];
1346 Callable<Void> action = callbacks.get(buttonAction);
1348 if (buttons != null && buttons.length > i && buttons[i] != null)
1356 jb.setText((String) o);
1357 jb.addActionListener(new ActionListener()
1360 public void actionPerformed(ActionEvent e)
1362 joptionpane.setValue(buttonAction);
1364 Executors.newSingleThreadExecutor().submit(action);
1365 // joptionpane.transferFocusBackward();
1366 joptionpane.transferFocusBackward();
1367 joptionpane.setVisible(false);
1368 // put focus and raise parent window if possible, unless cancel
1370 boolean raiseParent = (parentComponent != null);
1371 if (buttonAction == JOptionPane.CANCEL_OPTION)
1372 raiseParent = false;
1373 if (optionType == JOptionPane.YES_NO_OPTION
1374 && buttonAction == JOptionPane.NO_OPTION)
1375 raiseParent = false;
1378 parentComponent.requestFocus();
1379 if (parentComponent instanceof JInternalFrame)
1381 JInternalFrame jif = (JInternalFrame) parentComponent;
1386 else if (parentComponent instanceof Window)
1388 Window w = (Window) parentComponent;
1393 joptionpane.setVisible(false);
1396 optionsButtons[i] = jb;
1397 if (o.equals(initialValue))
1398 initialValueButton = jb;
1401 joptionpane.setMessage(message);
1402 joptionpane.setMessageType(messageType);
1403 joptionpane.setOptionType(optionType);
1404 joptionpane.setIcon(icon);
1405 joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1406 joptionpane.setInitialValue(
1407 Platform.isJS() ? initialValue : initialValueButton);
1409 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1410 dialog.setIconImage(null);
1411 dialog.setModalityType(
1412 modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1413 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1418 * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1420 * returns true if button was found
1422 public static boolean clickButton(JFrame frame, int buttonType)
1429 * This helper method makes the JInternalFrame wait until it is notified by an
1430 * InternalFrameClosing event. This method also adds the given JOptionPane to
1431 * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1435 * The JInternalFrame to make modal.
1437 private static void startModal(JInternalFrame f)
1439 // We need to add an additional glasspane-like component directly
1440 // below the frame, which intercepts all mouse events that are not
1441 // directed at the frame itself.
1442 JPanel modalInterceptor = new JPanel();
1443 modalInterceptor.setOpaque(false);
1444 JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1445 lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1446 modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1447 modalInterceptor.addMouseListener(new MouseAdapter()
1450 modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1453 lp.add(modalInterceptor);
1456 // We need to explicitly dispatch events when we are blocking the event
1458 EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1461 while (!f.isClosed())
1463 if (EventQueue.isDispatchThread())
1465 // The getNextEventMethod() issues wait() when no
1466 // event is available, so we don't need do explicitly wait().
1467 AWTEvent ev = queue.getNextEvent();
1468 // This mimics EventQueue.dispatchEvent(). We can't use
1469 // EventQueue.dispatchEvent() directly, because it is
1470 // protected, unfortunately.
1471 if (ev instanceof ActiveEvent)
1472 ((ActiveEvent) ev).dispatch();
1473 else if (ev.getSource() instanceof Component)
1474 ((Component) ev.getSource()).dispatchEvent(ev);
1475 else if (ev.getSource() instanceof MenuComponent)
1476 ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1477 // Other events are ignored as per spec in
1478 // EventQueue.dispatchEvent
1482 // Give other threads a chance to become active.
1486 } catch (InterruptedException ex)
1488 // If we get interrupted, then leave the modal state.
1491 // Clean up the modal interceptor.
1492 lp.remove(modalInterceptor);
1494 // Remove the internal frame from its parent, so it is no longer
1495 // lurking around and clogging memory.
1496 Container parent = f.getParent();