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;
87 public static int showConfirmDialog(Component parentComponent,
88 Object message) throws HeadlessException
90 // only called by test
91 return isInteractiveMode()
92 ? JOptionPane.showConfirmDialog(parentComponent, message)
93 : (int) getMockResponse();
97 * Message, title, optionType
99 * @param parentComponent
104 * @throws HeadlessException
106 public static int showConfirmDialog(Component parentComponent,
107 Object message, String title, int optionType)
108 throws HeadlessException
110 if (!isInteractiveMode())
112 return (int) getMockResponse();
116 case JvOptionPane.YES_NO_CANCEL_OPTION:
117 // FeatureRenderer amendFeatures ?? TODO ??
122 case JvOptionPane.YES_NO_OPTION:
123 // PromptUserConfig usage stats
124 // for now treated as "OK CANCEL"
126 case JvOptionPane.OK_CANCEL_OPTION:
127 // will fall back to simple HTML
128 return JOptionPane.showConfirmDialog(parentComponent, message, title,
134 * Adds a message type. Fallback is to just add it in the beginning.
136 * @param parentComponent
142 * @throws HeadlessException
144 public static int showConfirmDialog(Component parentComponent,
145 Object message, String title, int optionType, int messageType)
146 throws HeadlessException
148 // JalviewServicesChanged
149 // PromptUserConfig raiseDialog
150 return isInteractiveMode()
151 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
152 optionType, messageType)
153 : (int) getMockResponse();
159 * @param parentComponent
166 * @throws HeadlessException
168 public static int showConfirmDialog(Component parentComponent,
169 Object message, String title, int optionType, int messageType,
170 Icon icon) throws HeadlessException
172 // JvOptionPaneTest only
173 return isInteractiveMode()
174 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
175 optionType, messageType, icon)
176 : (int) getMockResponse();
180 * Internal version "OK"
182 * @param parentComponent
186 public static int showInternalConfirmDialog(Component parentComponent,
189 // JvOptionPaneTest only;
190 return isInteractiveMode()
191 ? JOptionPane.showInternalConfirmDialog(parentComponent,
193 : (int) getMockResponse();
197 * Internal version -- changed to standard version for now
199 * @param parentComponent
205 public static int showInternalConfirmDialog(Component parentComponent,
206 String message, String title, int optionType)
208 if (!isInteractiveMode())
210 return (int) getMockResponse();
214 case JvOptionPane.YES_NO_CANCEL_OPTION:
215 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
216 case JvOptionPane.YES_NO_OPTION:
217 // UserDefinedColoursSave -- relevant? TODO
220 case JvOptionPane.OK_CANCEL_OPTION:
222 // EditNameDialog --- uses panel for messsage TODO
224 // Desktop.inputURLMenuItem
226 return JOptionPane.showConfirmDialog(parentComponent, message, title,
233 * @param parentComponent
240 public static int showInternalConfirmDialog(Component parentComponent,
241 Object message, String title, int optionType, int messageType)
243 if (!isInteractiveMode())
245 return (int) getMockResponse();
249 case JvOptionPane.YES_NO_CANCEL_OPTION:
250 case JvOptionPane.YES_NO_OPTION:
251 // UserQuestionanaireCheck
255 case JvOptionPane.OK_CANCEL_OPTION:
256 // will fall back to simple HTML
257 return JOptionPane.showConfirmDialog(parentComponent, message, title,
258 optionType, messageType);
263 * adds icon; no longer internal
265 * @param parentComponent
273 public static int showInternalConfirmDialog(Component parentComponent,
274 Object message, String title, int optionType, int messageType,
277 if (!isInteractiveMode())
279 return (int) getMockResponse();
283 case JvOptionPane.YES_NO_CANCEL_OPTION:
284 case JvOptionPane.YES_NO_OPTION:
287 case JvOptionPane.OK_CANCEL_OPTION:
288 // Preferences editLink/newLink
289 return JOptionPane.showConfirmDialog(parentComponent, message, title,
290 optionType, messageType, icon);
296 * custom options full-featured
298 * @param parentComponent
305 * @param initialValue
307 * @throws HeadlessException
309 public static int showOptionDialog(Component parentComponent,
310 String message, String title, int optionType, int messageType,
311 Icon icon, Object[] options, Object initialValue)
312 throws HeadlessException
314 if (!isInteractiveMode())
316 return (int) getMockResponse();
322 // 1) AlignViewport for openLinkedAlignment
324 // Show a dialog with the option to open and link (cDNA <-> protein) as a
326 // alignment, either as a standalone alignment or in a split frame. Returns
327 // true if the new alignment was opened, false if not, because the user
328 // declined the offer.
330 // 2) UserDefinedColors warning about saving over a name already defined
332 return JOptionPane.showOptionDialog(parentComponent, message, title,
333 optionType, messageType, icon, options, initialValue);
340 * @throws HeadlessException
342 public static void showMessageDialog(Component parentComponent,
343 String message) throws HeadlessException
345 if (!isInteractiveMode())
347 outputMessage(message);
353 JOptionPane.showMessageDialog(parentComponent, message);
357 * OK with message, title, and type
359 * @param parentComponent
363 * @throws HeadlessException
365 public static void showMessageDialog(Component parentComponent,
366 String message, String title, int messageType)
367 throws HeadlessException
369 // 30 implementations -- all just fine.
371 if (!isInteractiveMode())
373 outputMessage(message);
377 JOptionPane.showMessageDialog(parentComponent,
378 getPrefix(messageType) + message, title, messageType);
382 * adds title and icon
384 * @param parentComponent
389 * @throws HeadlessException
391 public static void showMessageDialog(Component parentComponent,
392 String message, String title, int messageType, Icon icon)
393 throws HeadlessException
398 if (!isInteractiveMode())
400 outputMessage(message);
404 JOptionPane.showMessageDialog(parentComponent, message, title,
412 public static void showInternalMessageDialog(Component parentComponent,
416 // WsPreferences only
418 if (!isInteractiveMode())
420 outputMessage(message);
424 JOptionPane.showMessageDialog(parentComponent, message);
428 * Adds title and messageType
430 * @param parentComponent
435 public static void showInternalMessageDialog(Component parentComponent,
436 String message, String title, int messageType)
441 if (!isInteractiveMode())
443 outputMessage(message);
447 JOptionPane.showMessageDialog(parentComponent,
448 getPrefix(messageType) + message, title, messageType);
453 * @param parentComponent
459 public static void showInternalMessageDialog(Component parentComponent,
460 Object message, String title, int messageType, Icon icon)
465 if (!isInteractiveMode())
467 outputMessage(message);
471 JOptionPane.showMessageDialog(parentComponent, message, title,
479 * @throws HeadlessException
481 public static String showInputDialog(Object message)
482 throws HeadlessException
486 if (!isInteractiveMode())
488 return getMockResponse().toString();
491 return JOptionPane.showInputDialog(message);
495 * adds inital selection value
498 * @param initialSelectionValue
501 public static String showInputDialog(String message,
502 String initialSelectionValue)
504 if (!isInteractiveMode())
506 return getMockResponse().toString();
509 // AnnotationPanel character option
511 return JOptionPane.showInputDialog(message, initialSelectionValue);
515 * adds inital selection value
518 * @param initialSelectionValue
521 public static String showInputDialog(Object message,
522 Object initialSelectionValue)
524 if (!isInteractiveMode())
526 return getMockResponse().toString();
529 // AnnotationPanel character option
531 return JOptionPane.showInputDialog(message, initialSelectionValue);
537 * @param parentComponent
540 * @throws HeadlessException
542 public static String showInputDialog(Component parentComponent,
543 String message) throws HeadlessException
547 return isInteractiveMode()
548 ? JOptionPane.showInputDialog(parentComponent, message)
549 : getMockResponse().toString();
553 * input with initial selection
555 * @param parentComponent
557 * @param initialSelectionValue
560 public static String showInputDialog(Component parentComponent,
561 String message, String initialSelectionValue)
566 return isInteractiveMode()
567 ? JOptionPane.showInputDialog(parentComponent, message,
568 initialSelectionValue)
569 : getMockResponse().toString();
573 * input with initial selection
575 * @param parentComponent
577 * @param initialSelectionValue
580 public static String showInputDialog(Component parentComponent,
581 Object message, Object initialSelectionValue)
586 return isInteractiveMode()
587 ? JOptionPane.showInputDialog(parentComponent, message,
588 initialSelectionValue)
589 : getMockResponse().toString();
594 * @param parentComponent
599 * @throws HeadlessException
601 public static String showInputDialog(Component parentComponent,
602 String message, String title, int messageType)
603 throws HeadlessException
608 return isInteractiveMode()
609 ? JOptionPane.showInputDialog(parentComponent, message, title,
611 : getMockResponse().toString();
615 * Customized input option
617 * @param parentComponent
622 * @param selectionValues
623 * @param initialSelectionValue
625 * @throws HeadlessException
627 public static Object showInputDialog(Component parentComponent,
628 Object message, String title, int messageType, Icon icon,
629 Object[] selectionValues, Object initialSelectionValue)
630 throws HeadlessException
635 return isInteractiveMode()
636 ? JOptionPane.showInputDialog(parentComponent, message, title,
637 messageType, icon, selectionValues,
638 initialSelectionValue)
639 : getMockResponse().toString();
645 * @param parentComponent
649 public static String showInternalInputDialog(Component parentComponent,
654 return isInteractiveMode()
655 ? JOptionPane.showInternalInputDialog(parentComponent, message)
656 : getMockResponse().toString();
660 * internal with title and messageType
662 * @param parentComponent
668 public static String showInternalInputDialog(Component parentComponent,
669 String message, String title, int messageType)
672 // AlignFrame tabbedPane_mousePressed
674 return isInteractiveMode()
675 ? JOptionPane.showInternalInputDialog(parentComponent,
676 getPrefix(messageType) + message, title, messageType)
677 : getMockResponse().toString();
681 * customized internal
683 * @param parentComponent
688 * @param selectionValues
689 * @param initialSelectionValue
692 public static Object showInternalInputDialog(Component parentComponent,
693 String message, String title, int messageType, Icon icon,
694 Object[] selectionValues, Object initialSelectionValue)
698 return isInteractiveMode()
699 ? JOptionPane.showInternalInputDialog(parentComponent, message,
700 title, messageType, icon, selectionValues,
701 initialSelectionValue)
702 : getMockResponse().toString();
705 ///////////// end of options ///////////////
707 private static void outputMessage(Object message)
709 System.out.println(">>> JOption Message : " + message.toString());
712 public static Object getMockResponse()
717 public static void setMockResponse(Object mockOption)
719 JvOptionPane.mockResponse = mockOption;
722 public static void resetMock()
724 setMockResponse(JvOptionPane.CANCEL_OPTION);
725 setInteractiveMode(true);
728 public static boolean isInteractiveMode()
730 return interactiveMode;
733 public static void setInteractiveMode(boolean interactive)
735 JvOptionPane.interactiveMode = interactive;
738 private static String getPrefix(int messageType)
747 case JvOptionPane.WARNING_MESSAGE:
748 prefix = "WARNING! ";
750 case JvOptionPane.ERROR_MESSAGE:
761 * create a new option dialog that can be used to register responses - along
762 * lines of showOptionDialog
767 * @param defaultOption
768 * @param plainMessage
774 public static JvOptionPane newOptionDialog()
776 return new JvOptionPane(null);
779 public static JvOptionPane newOptionDialog(Component parentComponent)
781 return new JvOptionPane(parentComponent);
784 public void showDialog(String message, String title, int optionType,
785 int messageType, Icon icon, Object[] options, Object initialValue)
787 showDialog(message, title, optionType, messageType, icon, options,
791 public void showDialog(Object message, String title, int optionType,
792 int messageType, Icon icon, Object[] options, Object initialValue,
795 showDialog(message, title, optionType, messageType, icon, options,
796 initialValue, modal, null);
799 public void showDialog(Object message, String title, int optionType,
800 int messageType, Icon icon, Object[] options, Object initialValue,
801 boolean modal, JButton[] buttons)
803 if (!isInteractiveMode())
805 handleResponse(getMockResponse());
812 // 1) AlignViewport for openLinkedAlignment
814 // Show a dialog with the option to open and link (cDNA <-> protein) as a
816 // alignment, either as a standalone alignment or in a split frame. Returns
817 // true if the new alignment was opened, false if not, because the user
818 // declined the offer.
820 // 2) UserDefinedColors warning about saving over a name already defined
823 ourOptions = Arrays.asList(options);
827 boolean useButtons = false;
828 Object initialValueButton = null;
829 NOTNULL: if (buttons != null)
831 if (buttons.length != options.length)
833 jalview.bin.Console.error(
834 "Supplied buttons array not the same length as supplied options array.");
837 int[] buttonActions = { JOptionPane.YES_OPTION,
838 JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
839 for (int i = 0; i < options.length; i++)
841 Object o = options[i];
842 jalview.bin.Console.debug(
843 "Setting button " + i + " to '" + o.toString() + "'");
844 JButton jb = buttons[i];
846 if (o.equals(initialValue))
847 initialValueButton = jb;
849 int buttonAction = buttonActions[i];
850 Callable<Void> action = callbacks.get(buttonAction);
851 jb.setText((String) o);
852 jb.addActionListener(new ActionListener()
855 public void actionPerformed(ActionEvent e)
858 Object obj = e.getSource();
859 if (obj == null || !(obj instanceof Component))
861 jalview.bin.Console.debug(
862 "Could not find Component source of event object "
866 Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
867 JOptionPane.class, (Component) obj);
868 if (joptionpaneObject == null
869 || !(joptionpaneObject instanceof JOptionPane))
871 jalview.bin.Console.debug(
872 "Could not find JOptionPane ancestor of event object "
876 JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
877 joptionpane.setValue(buttonAction);
879 Executors.newSingleThreadExecutor().submit(action);
880 joptionpane.transferFocusBackward();
881 joptionpane.setVisible(false);
882 // put focus and raise parent window if possible, unless cancel or
884 boolean raiseParent = (parentComponent != null);
885 if (buttonAction == JOptionPane.CANCEL_OPTION)
887 if (optionType == JOptionPane.YES_NO_OPTION
888 && buttonAction == JOptionPane.NO_OPTION)
892 parentComponent.requestFocus();
893 if (parentComponent instanceof JInternalFrame)
895 JInternalFrame jif = (JInternalFrame) parentComponent;
900 else if (parentComponent instanceof Window)
902 Window w = (Window) parentComponent;
907 joptionpane.setVisible(false);
914 // use a JOptionPane as usual
915 int response = JOptionPane.showOptionDialog(parentComponent, message,
916 title, optionType, messageType, icon,
917 useButtons ? buttons : options,
918 useButtons ? initialValueButton : initialValue);
921 * In Java, the response is returned to this thread and handled here; (for
922 * Javascript, see propertyChange)
924 if (!Platform.isJS())
931 handleResponse(response);
937 * This is java similar to the swingjs handling, with the callbacks attached to
938 * the button press of the dialog. This means we can use a non-modal JDialog for
939 * the confirmation without blocking the GUI.
941 JOptionPane joptionpane = new JOptionPane();
942 // Make button options
943 int[] buttonActions = { JvOptionPane.YES_OPTION,
944 JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
946 // we need the strings to make the buttons with actionEventListener
949 ArrayList<String> options_default = new ArrayList<>();
951 .add(UIManager.getString("OptionPane.yesButtonText"));
952 if (optionType == JvOptionPane.YES_NO_OPTION
953 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
956 .add(UIManager.getString("OptionPane.noButtonText"));
958 if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
961 .add(UIManager.getString("OptionPane.cancelButtonText"));
963 options = options_default.toArray();
966 ArrayList<JButton> options_btns = new ArrayList<>();
967 Object initialValue_btn = null;
968 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
971 for (int i = 0; i < options.length && i < 3; i++)
973 Object o = options[i];
974 int buttonAction = buttonActions[i];
975 Callable<Void> action = callbacks.get(buttonAction);
976 JButton jb = new JButton();
977 jb.setText((String) o);
978 jb.addActionListener(new ActionListener()
981 public void actionPerformed(ActionEvent e)
983 joptionpane.setValue(buttonAction);
985 Executors.newSingleThreadExecutor().submit(action);
986 // joptionpane.transferFocusBackward();
987 joptionpane.transferFocusBackward();
988 joptionpane.setVisible(false);
989 // put focus and raise parent window if possible, unless cancel
991 boolean raiseParent = (parentComponent != null);
992 if (buttonAction == JvOptionPane.CANCEL_OPTION)
994 if (optionType == JvOptionPane.YES_NO_OPTION
995 && buttonAction == JvOptionPane.NO_OPTION)
999 parentComponent.requestFocus();
1000 if (parentComponent instanceof JInternalFrame)
1002 JInternalFrame jif = (JInternalFrame) parentComponent;
1007 else if (parentComponent instanceof Window)
1009 Window w = (Window) parentComponent;
1014 joptionpane.setVisible(false);
1017 options_btns.add(jb);
1018 if (o.equals(initialValue))
1019 initialValue_btn = jb;
1022 joptionpane.setMessage(message);
1023 joptionpane.setMessageType(messageType);
1024 joptionpane.setOptionType(optionType);
1025 joptionpane.setIcon(icon);
1026 joptionpane.setOptions(
1027 Platform.isJS() ? options : options_btns.toArray());
1028 joptionpane.setInitialValue(
1029 Platform.isJS() ? initialValue : initialValue_btn);
1031 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1032 dialog.setIconImage(WindowIcons.logoIcon.getImage());
1033 dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1034 : ModalityType.MODELESS);
1035 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1036 dialog.setVisible(true);
1040 public void showInternalDialog(JPanel mainPanel, String title,
1041 int yesNoCancelOption, int questionMessage, Icon icon,
1042 Object[] options, String initresponse)
1044 if (!isInteractiveMode())
1046 handleResponse(getMockResponse());
1049 // need to set these separately so we can set the title bar icon later
1050 this.setOptionType(yesNoCancelOption);
1051 this.setMessageType(questionMessage);
1053 this.setInitialValue(initresponse);
1054 this.setOptions(options);
1055 this.setMessage(mainPanel);
1057 ourOptions = Arrays.asList(options);
1059 if (parentComponent != this)
1061 JInternalFrame jif = this.createInternalFrame(parentComponent, title);
1062 jif.setFrameIcon(WindowIcons.logoIcon);
1063 jif.addInternalFrameListener(new InternalFrameListener()
1066 public void internalFrameActivated(InternalFrameEvent arg0)
1071 public void internalFrameClosed(InternalFrameEvent arg0)
1073 JvOptionPane.this.internalDialogHandleResponse();
1077 public void internalFrameClosing(InternalFrameEvent arg0)
1082 public void internalFrameDeactivated(InternalFrameEvent arg0)
1087 public void internalFrameDeiconified(InternalFrameEvent arg0)
1092 public void internalFrameIconified(InternalFrameEvent arg0)
1097 public void internalFrameOpened(InternalFrameEvent arg0)
1101 jif.setVisible(true);
1107 JDialog dialog = this.createDialog(parentComponent, title);
1108 dialog.setIconImage(WindowIcons.logoIcon.getImage());
1109 dialog.setVisible(true); // blocking
1110 this.internalDialogHandleResponse();
1115 private void internalDialogHandleResponse()
1117 String responseString = (String) this.getValue();
1118 int response = ourOptions.indexOf(responseString);
1120 if (!Platform.isJS())
1127 handleResponse(response);
1132 * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1133 * action) { callbacks.put(response, new Callable<Void>() {
1135 * @Override public Void call() { action.run(); return null; } }); return this;
1139 public JvOptionPane setResponseHandler(Object response,
1140 Callable<Void> action)
1142 callbacks.put(response, action);
1147 * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1150 public static int showDialogOnTop(String label, String actionString,
1151 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1153 if (!isInteractiveMode())
1155 return (int) getMockResponse();
1157 // Ensure Jalview window is brought to front (primarily for Quit
1158 // confirmation window to be visible)
1160 // This method of raising the Jalview window is broken in java
1161 // jalviewDesktop.setVisible(true);
1162 // jalviewDesktop.toFront();
1164 // A better hack which works is to create a new JFrame parent with
1165 // setAlwaysOnTop(true)
1166 JFrame dialogParent = new JFrame();
1167 dialogParent.setIconImage(WindowIcons.logoIcon.getImage());
1168 dialogParent.setAlwaysOnTop(true);
1170 int answer = JOptionPane.showConfirmDialog(dialogParent, label,
1171 actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
1173 dialogParent.setAlwaysOnTop(false);
1174 dialogParent.dispose();
1179 public void showDialogOnTopAsync(String label, String actionString,
1180 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1181 Object[] options, Object initialValue, boolean modal)
1183 JFrame frame = new JFrame();
1184 frame.setIconImage(WindowIcons.logoIcon.getImage());
1185 showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1186 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1189 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1190 String actionString, int JOPTIONPANE_OPTION,
1191 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1192 Object initialValue, boolean modal)
1194 showDialogOnTopAsync(dialogParent, label, actionString,
1195 JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1196 initialValue, modal, null);
1199 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1200 String actionString, int JOPTIONPANE_OPTION,
1201 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1202 Object initialValue, boolean modal, JButton[] buttons)
1204 if (!isInteractiveMode())
1206 handleResponse(getMockResponse());
1209 // Ensure Jalview window is brought to front (primarily for Quit
1210 // confirmation window to be visible)
1212 // This method of raising the Jalview window is broken in java
1213 // jalviewDesktop.setVisible(true);
1214 // jalviewDesktop.toFront();
1216 // A better hack which works is to create a new JFrame parent with
1217 // setAlwaysOnTop(true)
1218 dialogParent.setAlwaysOnTop(true);
1219 parentComponent = dialogParent;
1221 showDialog(label, actionString, JOPTIONPANE_OPTION,
1222 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1225 dialogParent.setAlwaysOnTop(false);
1226 dialogParent.dispose();
1230 * JalviewJS signals option selection by a property change event for the
1231 * option e.g. "OK". This methods responds to that by running the response
1232 * action that corresponds to that option.
1237 public void propertyChange(PropertyChangeEvent evt)
1239 Object newValue = evt.getNewValue();
1240 int ourOption = ourOptions.indexOf(newValue);
1243 handleResponse(ourOption);
1248 handleResponse(newValue);
1253 public void handleResponse(Object response)
1256 * this test is for NaN in Chrome
1258 if (response != null && !response.equals(response))
1262 Callable<Void> action = callbacks.get(response);
1268 } catch (Exception e)
1270 e.printStackTrace();
1272 if (parentComponent != null)
1273 parentComponent.requestFocus();
1278 * Create a non-modal confirm dialog
1280 public JDialog createDialog(Component parentComponent, Object message,
1281 String title, int optionType, int messageType, Icon icon,
1282 Object[] options, Object initialValue, boolean modal)
1284 return createDialog(parentComponent, message, title, optionType,
1285 messageType, icon, options, initialValue, modal, null);
1288 public JDialog createDialog(Component parentComponent, Object message,
1289 String title, int optionType, int messageType, Icon icon,
1290 Object[] options, Object initialValue, boolean modal,
1293 if (!isInteractiveMode())
1295 handleResponse(getMockResponse());
1298 JButton[] optionsButtons = null;
1299 Object initialValueButton = null;
1300 JOptionPane joptionpane = new JOptionPane();
1301 // Make button options
1302 int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1303 JOptionPane.CANCEL_OPTION };
1305 // we need the strings to make the buttons with actionEventListener
1306 if (options == null)
1308 ArrayList<String> options_default = new ArrayList<>();
1309 options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1310 if (optionType == JOptionPane.YES_NO_OPTION
1311 || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1313 options_default.add(UIManager.getString("OptionPane.noButtonText"));
1315 if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1318 .add(UIManager.getString("OptionPane.cancelButtonText"));
1320 options = options_default.toArray();
1322 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1325 if (((optionType == JOptionPane.YES_OPTION
1326 || optionType == JOptionPane.NO_OPTION
1327 || optionType == JOptionPane.CANCEL_OPTION
1328 || optionType == JOptionPane.OK_OPTION
1329 || optionType == JOptionPane.DEFAULT_OPTION)
1330 && options.length < 1)
1331 || ((optionType == JOptionPane.YES_NO_OPTION
1332 || optionType == JOptionPane.OK_CANCEL_OPTION)
1333 && options.length < 2)
1334 || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1335 && options.length < 3))
1338 .debug("JvOptionPane: not enough options for dialog type");
1340 optionsButtons = new JButton[options.length];
1341 for (int i = 0; i < options.length && i < 3; i++)
1343 Object o = options[i];
1344 int buttonAction = buttonActions[i];
1345 Callable<Void> action = callbacks.get(buttonAction);
1347 if (buttons != null && buttons.length > i && buttons[i] != null)
1355 jb.setText((String) o);
1356 jb.addActionListener(new ActionListener()
1359 public void actionPerformed(ActionEvent e)
1361 joptionpane.setValue(buttonAction);
1363 Executors.newSingleThreadExecutor().submit(action);
1364 // joptionpane.transferFocusBackward();
1365 joptionpane.transferFocusBackward();
1366 joptionpane.setVisible(false);
1367 // put focus and raise parent window if possible, unless cancel
1369 boolean raiseParent = (parentComponent != null);
1370 if (buttonAction == JOptionPane.CANCEL_OPTION)
1371 raiseParent = false;
1372 if (optionType == JOptionPane.YES_NO_OPTION
1373 && buttonAction == JOptionPane.NO_OPTION)
1374 raiseParent = false;
1377 parentComponent.requestFocus();
1378 if (parentComponent instanceof JInternalFrame)
1380 JInternalFrame jif = (JInternalFrame) parentComponent;
1385 else if (parentComponent instanceof Window)
1387 Window w = (Window) parentComponent;
1392 joptionpane.setVisible(false);
1395 optionsButtons[i] = jb;
1396 if (o.equals(initialValue))
1397 initialValueButton = jb;
1400 joptionpane.setMessage(message);
1401 joptionpane.setMessageType(messageType);
1402 joptionpane.setOptionType(optionType);
1403 joptionpane.setIcon(icon);
1404 joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1405 joptionpane.setInitialValue(
1406 Platform.isJS() ? initialValue : initialValueButton);
1408 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1409 dialog.setIconImage(WindowIcons.logoIcon.getImage());
1410 dialog.setModalityType(
1411 modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1412 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1417 * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1419 * returns true if button was found
1421 public static boolean clickButton(JFrame frame, int buttonType)
1428 * This helper method makes the JInternalFrame wait until it is notified by an
1429 * InternalFrameClosing event. This method also adds the given JOptionPane to
1430 * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1434 * The JInternalFrame to make modal.
1436 private static void startModal(JInternalFrame f)
1438 // We need to add an additional glasspane-like component directly
1439 // below the frame, which intercepts all mouse events that are not
1440 // directed at the frame itself.
1441 JPanel modalInterceptor = new JPanel();
1442 modalInterceptor.setOpaque(false);
1443 JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1444 lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1445 modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1446 modalInterceptor.addMouseListener(new MouseAdapter()
1449 modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1452 lp.add(modalInterceptor);
1455 // We need to explicitly dispatch events when we are blocking the event
1457 EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1460 while (!f.isClosed())
1462 if (EventQueue.isDispatchThread())
1464 // The getNextEventMethod() issues wait() when no
1465 // event is available, so we don't need do explicitly wait().
1466 AWTEvent ev = queue.getNextEvent();
1467 // This mimics EventQueue.dispatchEvent(). We can't use
1468 // EventQueue.dispatchEvent() directly, because it is
1469 // protected, unfortunately.
1470 if (ev instanceof ActiveEvent)
1471 ((ActiveEvent) ev).dispatch();
1472 else if (ev.getSource() instanceof Component)
1473 ((Component) ev.getSource()).dispatchEvent(ev);
1474 else if (ev.getSource() instanceof MenuComponent)
1475 ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1476 // Other events are ignored as per spec in
1477 // EventQueue.dispatchEvent
1481 // Give other threads a chance to become active.
1485 } catch (InterruptedException ex)
1487 // If we get interrupted, then leave the modal state.
1490 // Clean up the modal interceptor.
1491 lp.remove(modalInterceptor);
1493 // Remove the internal frame from its parent, so it is no longer
1494 // lurking around and clogging memory.
1495 Container parent = f.getParent();