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.ChannelProperties;
61 import jalview.util.Platform;
62 import jalview.util.dialogrunner.DialogRunnerI;
64 public class JvOptionPane extends JOptionPane
65 implements DialogRunnerI, PropertyChangeListener
67 private static final long serialVersionUID = -3019167117756785229L;
69 private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
71 private static boolean interactiveMode = true;
73 private Component parentComponent;
75 private Map<Object, Callable<Void>> callbacks = new HashMap<>();
78 * JalviewJS reports user choice in the dialog as the selected option (text);
79 * this list allows conversion to index (int)
81 List<Object> ourOptions;
83 public JvOptionPane(final Component parent)
85 this.parentComponent = Platform.isJS() ? this : parent;
89 public static int showConfirmDialog(Component parentComponent,
90 Object message) throws HeadlessException
92 // only called by test
93 return isInteractiveMode()
94 ? JOptionPane.showConfirmDialog(parentComponent, message)
95 : (int) getMockResponse();
99 * Message, title, optionType
101 * @param parentComponent
106 * @throws HeadlessException
108 public static int showConfirmDialog(Component parentComponent,
109 Object message, String title, int optionType)
110 throws HeadlessException
112 if (!isInteractiveMode())
114 return (int) getMockResponse();
118 case JvOptionPane.YES_NO_CANCEL_OPTION:
119 // FeatureRenderer amendFeatures ?? TODO ??
124 case JvOptionPane.YES_NO_OPTION:
125 // PromptUserConfig usage stats
126 // for now treated as "OK CANCEL"
128 case JvOptionPane.OK_CANCEL_OPTION:
129 // will fall back to simple HTML
130 return JOptionPane.showConfirmDialog(parentComponent, message, title,
136 * Adds a message type. Fallback is to just add it in the beginning.
138 * @param parentComponent
144 * @throws HeadlessException
146 public static int showConfirmDialog(Component parentComponent,
147 Object message, String title, int optionType, int messageType)
148 throws HeadlessException
150 // JalviewServicesChanged
151 // PromptUserConfig raiseDialog
152 return isInteractiveMode()
153 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
154 optionType, messageType)
155 : (int) getMockResponse();
161 * @param parentComponent
168 * @throws HeadlessException
170 public static int showConfirmDialog(Component parentComponent,
171 Object message, String title, int optionType, int messageType,
172 Icon icon) throws HeadlessException
174 // JvOptionPaneTest only
175 return isInteractiveMode()
176 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
177 optionType, messageType, icon)
178 : (int) getMockResponse();
182 * Internal version "OK"
184 * @param parentComponent
188 public static int showInternalConfirmDialog(Component parentComponent,
191 // JvOptionPaneTest only;
192 return isInteractiveMode()
193 ? JOptionPane.showInternalConfirmDialog(parentComponent,
195 : (int) getMockResponse();
199 * Internal version -- changed to standard version for now
201 * @param parentComponent
207 public static int showInternalConfirmDialog(Component parentComponent,
208 String message, String title, int optionType)
210 if (!isInteractiveMode())
212 return (int) getMockResponse();
216 case JvOptionPane.YES_NO_CANCEL_OPTION:
217 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
218 case JvOptionPane.YES_NO_OPTION:
219 // UserDefinedColoursSave -- relevant? TODO
222 case JvOptionPane.OK_CANCEL_OPTION:
224 // EditNameDialog --- uses panel for messsage TODO
226 // Desktop.inputURLMenuItem
228 return JOptionPane.showConfirmDialog(parentComponent, message, title,
235 * @param parentComponent
242 public static int showInternalConfirmDialog(Component parentComponent,
243 Object message, String title, int optionType, int messageType)
245 if (!isInteractiveMode())
247 return (int) getMockResponse();
251 case JvOptionPane.YES_NO_CANCEL_OPTION:
252 case JvOptionPane.YES_NO_OPTION:
253 // UserQuestionanaireCheck
257 case JvOptionPane.OK_CANCEL_OPTION:
258 // will fall back to simple HTML
259 return JOptionPane.showConfirmDialog(parentComponent, message, title,
260 optionType, messageType);
265 * adds icon; no longer internal
267 * @param parentComponent
275 public static int showInternalConfirmDialog(Component parentComponent,
276 Object message, String title, int optionType, int messageType,
279 if (!isInteractiveMode())
281 return (int) getMockResponse();
285 case JvOptionPane.YES_NO_CANCEL_OPTION:
286 case JvOptionPane.YES_NO_OPTION:
289 case JvOptionPane.OK_CANCEL_OPTION:
290 // Preferences editLink/newLink
291 return JOptionPane.showConfirmDialog(parentComponent, message, title,
292 optionType, messageType, icon);
298 * custom options full-featured
300 * @param parentComponent
307 * @param initialValue
309 * @throws HeadlessException
311 public static int showOptionDialog(Component parentComponent,
312 String message, String title, int optionType, int messageType,
313 Icon icon, Object[] options, Object initialValue)
314 throws HeadlessException
316 if (!isInteractiveMode())
318 return (int) getMockResponse();
324 // 1) AlignViewport for openLinkedAlignment
326 // Show a dialog with the option to open and link (cDNA <-> protein) as a
328 // alignment, either as a standalone alignment or in a split frame. Returns
329 // true if the new alignment was opened, false if not, because the user
330 // declined the offer.
332 // 2) UserDefinedColors warning about saving over a name already defined
334 return JOptionPane.showOptionDialog(parentComponent, message, title,
335 optionType, messageType, icon, options, initialValue);
342 * @throws HeadlessException
344 public static void showMessageDialog(Component parentComponent,
345 String message) throws HeadlessException
347 if (!isInteractiveMode())
349 outputMessage(message);
355 JOptionPane.showMessageDialog(parentComponent, message);
359 * OK with message, title, and type
361 * @param parentComponent
365 * @throws HeadlessException
367 public static void showMessageDialog(Component parentComponent,
368 String message, String title, int messageType)
369 throws HeadlessException
371 // 30 implementations -- all just fine.
373 if (!isInteractiveMode())
375 outputMessage(message);
379 JOptionPane.showMessageDialog(parentComponent,
380 getPrefix(messageType) + message, title, messageType);
384 * adds title and icon
386 * @param parentComponent
391 * @throws HeadlessException
393 public static void showMessageDialog(Component parentComponent,
394 String message, String title, int messageType, Icon icon)
395 throws HeadlessException
400 if (!isInteractiveMode())
402 outputMessage(message);
406 JOptionPane.showMessageDialog(parentComponent, message, title,
414 public static void showInternalMessageDialog(Component parentComponent,
418 // WsPreferences only
420 if (!isInteractiveMode())
422 outputMessage(message);
426 JOptionPane.showMessageDialog(parentComponent, message);
430 * Adds title and messageType
432 * @param parentComponent
437 public static void showInternalMessageDialog(Component parentComponent,
438 String message, String title, int messageType)
443 if (!isInteractiveMode())
445 outputMessage(message);
449 JOptionPane.showMessageDialog(parentComponent,
450 getPrefix(messageType) + message, title, messageType);
455 * @param parentComponent
461 public static void showInternalMessageDialog(Component parentComponent,
462 Object message, String title, int messageType, Icon icon)
467 if (!isInteractiveMode())
469 outputMessage(message);
473 JOptionPane.showMessageDialog(parentComponent, message, title,
481 * @throws HeadlessException
483 public static String showInputDialog(Object message)
484 throws HeadlessException
488 if (!isInteractiveMode())
490 return getMockResponse().toString();
493 return JOptionPane.showInputDialog(message);
497 * adds inital selection value
500 * @param initialSelectionValue
503 public static String showInputDialog(String message,
504 String initialSelectionValue)
506 if (!isInteractiveMode())
508 return getMockResponse().toString();
511 // AnnotationPanel character option
513 return JOptionPane.showInputDialog(message, initialSelectionValue);
517 * adds inital selection value
520 * @param initialSelectionValue
523 public static String showInputDialog(Object message,
524 Object initialSelectionValue)
526 if (!isInteractiveMode())
528 return getMockResponse().toString();
531 // AnnotationPanel character option
533 return JOptionPane.showInputDialog(message, initialSelectionValue);
539 * @param parentComponent
542 * @throws HeadlessException
544 public static String showInputDialog(Component parentComponent,
545 String message) throws HeadlessException
549 return isInteractiveMode()
550 ? JOptionPane.showInputDialog(parentComponent, message)
551 : getMockResponse().toString();
555 * input with initial selection
557 * @param parentComponent
559 * @param initialSelectionValue
562 public static String showInputDialog(Component parentComponent,
563 String message, String initialSelectionValue)
568 return isInteractiveMode()
569 ? JOptionPane.showInputDialog(parentComponent, message,
570 initialSelectionValue)
571 : getMockResponse().toString();
575 * input with initial selection
577 * @param parentComponent
579 * @param initialSelectionValue
582 public static String showInputDialog(Component parentComponent,
583 Object message, Object initialSelectionValue)
588 return isInteractiveMode()
589 ? JOptionPane.showInputDialog(parentComponent, message,
590 initialSelectionValue)
591 : getMockResponse().toString();
596 * @param parentComponent
601 * @throws HeadlessException
603 public static String showInputDialog(Component parentComponent,
604 String message, String title, int messageType)
605 throws HeadlessException
610 return isInteractiveMode()
611 ? JOptionPane.showInputDialog(parentComponent, message, title,
613 : getMockResponse().toString();
617 * Customized input option
619 * @param parentComponent
624 * @param selectionValues
625 * @param initialSelectionValue
627 * @throws HeadlessException
629 public static Object showInputDialog(Component parentComponent,
630 Object message, String title, int messageType, Icon icon,
631 Object[] selectionValues, Object initialSelectionValue)
632 throws HeadlessException
637 return isInteractiveMode()
638 ? JOptionPane.showInputDialog(parentComponent, message, title,
639 messageType, icon, selectionValues,
640 initialSelectionValue)
641 : getMockResponse().toString();
647 * @param parentComponent
651 public static String showInternalInputDialog(Component parentComponent,
656 return isInteractiveMode()
657 ? JOptionPane.showInternalInputDialog(parentComponent, message)
658 : getMockResponse().toString();
662 * internal with title and messageType
664 * @param parentComponent
670 public static String showInternalInputDialog(Component parentComponent,
671 String message, String title, int messageType)
674 // AlignFrame tabbedPane_mousePressed
676 return isInteractiveMode()
677 ? JOptionPane.showInternalInputDialog(parentComponent,
678 getPrefix(messageType) + message, title, messageType)
679 : getMockResponse().toString();
683 * customized internal
685 * @param parentComponent
690 * @param selectionValues
691 * @param initialSelectionValue
694 public static Object showInternalInputDialog(Component parentComponent,
695 String message, String title, int messageType, Icon icon,
696 Object[] selectionValues, Object initialSelectionValue)
700 return isInteractiveMode()
701 ? JOptionPane.showInternalInputDialog(parentComponent, message,
702 title, messageType, icon, selectionValues,
703 initialSelectionValue)
704 : getMockResponse().toString();
707 ///////////// end of options ///////////////
709 private static void outputMessage(Object message)
711 System.out.println(">>> JOption Message : " + message.toString());
714 public static Object getMockResponse()
719 public static void setMockResponse(Object mockOption)
721 JvOptionPane.mockResponse = mockOption;
724 public static void resetMock()
726 setMockResponse(JvOptionPane.CANCEL_OPTION);
727 setInteractiveMode(true);
730 public static boolean isInteractiveMode()
732 return interactiveMode;
735 public static void setInteractiveMode(boolean interactive)
737 JvOptionPane.interactiveMode = interactive;
740 private static String getPrefix(int messageType)
749 case JvOptionPane.WARNING_MESSAGE:
750 prefix = "WARNING! ";
752 case JvOptionPane.ERROR_MESSAGE:
763 * create a new option dialog that can be used to register responses - along
764 * lines of showOptionDialog
769 * @param defaultOption
770 * @param plainMessage
776 public static JvOptionPane newOptionDialog()
778 return new JvOptionPane(null);
781 public static JvOptionPane newOptionDialog(Component parentComponent)
783 return new JvOptionPane(parentComponent);
786 public void showDialog(String message, String title, int optionType,
787 int messageType, Icon icon, Object[] options, Object initialValue)
789 showDialog(message, title, optionType, messageType, icon, options,
793 public void showDialog(Object message, String title, int optionType,
794 int messageType, Icon icon, Object[] options, Object initialValue,
797 showDialog(message, title, optionType, messageType, icon, options,
798 initialValue, modal, null);
801 public void showDialog(Object message, String title, int optionType,
802 int messageType, Icon icon, Object[] options, Object initialValue,
803 boolean modal, JButton[] buttons)
805 if (!isInteractiveMode())
807 handleResponse(getMockResponse());
814 // 1) AlignViewport for openLinkedAlignment
816 // Show a dialog with the option to open and link (cDNA <-> protein) as a
818 // alignment, either as a standalone alignment or in a split frame. Returns
819 // true if the new alignment was opened, false if not, because the user
820 // declined the offer.
822 // 2) UserDefinedColors warning about saving over a name already defined
825 ourOptions = Arrays.asList(options);
829 boolean useButtons = false;
830 Object initialValueButton = null;
831 NOTNULL: if (buttons != null)
833 if (buttons.length != options.length)
835 jalview.bin.Console.error(
836 "Supplied buttons array not the same length as supplied options array.");
839 int[] buttonActions = { JOptionPane.YES_OPTION,
840 JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
841 for (int i = 0; i < options.length; i++)
843 Object o = options[i];
844 jalview.bin.Console.debug(
845 "Setting button " + i + " to '" + o.toString() + "'");
846 JButton jb = buttons[i];
848 if (o.equals(initialValue))
849 initialValueButton = jb;
851 int buttonAction = buttonActions[i];
852 Callable<Void> action = callbacks.get(buttonAction);
853 jb.setText((String) o);
854 jb.addActionListener(new ActionListener()
857 public void actionPerformed(ActionEvent e)
860 Object obj = e.getSource();
861 if (obj == null || !(obj instanceof Component))
863 jalview.bin.Console.debug(
864 "Could not find Component source of event object "
868 Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
869 JOptionPane.class, (Component) obj);
870 if (joptionpaneObject == null
871 || !(joptionpaneObject instanceof JOptionPane))
873 jalview.bin.Console.debug(
874 "Could not find JOptionPane ancestor of event object "
878 JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
879 joptionpane.setValue(buttonAction);
881 Executors.newSingleThreadExecutor().submit(action);
882 joptionpane.transferFocusBackward();
883 joptionpane.setVisible(false);
884 // put focus and raise parent window if possible, unless cancel or
886 boolean raiseParent = (parentComponent != null);
887 if (buttonAction == JOptionPane.CANCEL_OPTION)
889 if (optionType == JOptionPane.YES_NO_OPTION
890 && buttonAction == JOptionPane.NO_OPTION)
894 parentComponent.requestFocus();
895 if (parentComponent instanceof JInternalFrame)
897 JInternalFrame jif = (JInternalFrame) parentComponent;
902 else if (parentComponent instanceof Window)
904 Window w = (Window) parentComponent;
909 joptionpane.setVisible(false);
916 // use a JOptionPane as usual
917 int response = JOptionPane.showOptionDialog(parentComponent, message,
918 title, optionType, messageType, icon,
919 useButtons ? buttons : options,
920 useButtons ? initialValueButton : initialValue);
923 * In Java, the response is returned to this thread and handled here; (for
924 * Javascript, see propertyChange)
926 if (!Platform.isJS())
933 handleResponse(response);
939 * This is java similar to the swingjs handling, with the callbacks attached to
940 * the button press of the dialog. This means we can use a non-modal JDialog for
941 * the confirmation without blocking the GUI.
943 JOptionPane joptionpane = new JOptionPane();
944 // Make button options
945 int[] buttonActions = { JvOptionPane.YES_OPTION,
946 JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
948 // we need the strings to make the buttons with actionEventListener
951 ArrayList<String> options_default = new ArrayList<>();
953 .add(UIManager.getString("OptionPane.yesButtonText"));
954 if (optionType == JvOptionPane.YES_NO_OPTION
955 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
958 .add(UIManager.getString("OptionPane.noButtonText"));
960 if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
963 .add(UIManager.getString("OptionPane.cancelButtonText"));
965 options = options_default.toArray();
968 ArrayList<JButton> options_btns = new ArrayList<>();
969 Object initialValue_btn = null;
970 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
973 for (int i = 0; i < options.length && i < 3; i++)
975 Object o = options[i];
976 int buttonAction = buttonActions[i];
977 Callable<Void> action = callbacks.get(buttonAction);
978 JButton jb = new JButton();
979 jb.setText((String) o);
980 jb.addActionListener(new ActionListener()
983 public void actionPerformed(ActionEvent e)
985 joptionpane.setValue(buttonAction);
987 Executors.newSingleThreadExecutor().submit(action);
988 // joptionpane.transferFocusBackward();
989 joptionpane.transferFocusBackward();
990 joptionpane.setVisible(false);
991 // put focus and raise parent window if possible, unless cancel
993 boolean raiseParent = (parentComponent != null);
994 if (buttonAction == JvOptionPane.CANCEL_OPTION)
996 if (optionType == JvOptionPane.YES_NO_OPTION
997 && buttonAction == JvOptionPane.NO_OPTION)
1001 parentComponent.requestFocus();
1002 if (parentComponent instanceof JInternalFrame)
1004 JInternalFrame jif = (JInternalFrame) parentComponent;
1009 else if (parentComponent instanceof Window)
1011 Window w = (Window) parentComponent;
1016 joptionpane.setVisible(false);
1019 options_btns.add(jb);
1020 if (o.equals(initialValue))
1021 initialValue_btn = jb;
1024 joptionpane.setMessage(message);
1025 joptionpane.setMessageType(messageType);
1026 joptionpane.setOptionType(optionType);
1027 joptionpane.setIcon(icon);
1028 joptionpane.setOptions(
1029 Platform.isJS() ? options : options_btns.toArray());
1030 joptionpane.setInitialValue(
1031 Platform.isJS() ? initialValue : initialValue_btn);
1033 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1034 dialog.setIconImages(ChannelProperties.getIconList());
1035 dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1036 : ModalityType.MODELESS);
1037 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1038 dialog.setVisible(true);
1042 public void showInternalDialog(JPanel mainPanel, String title,
1043 int yesNoCancelOption, int questionMessage, Icon icon,
1044 Object[] options, String initresponse)
1046 if (!isInteractiveMode())
1048 handleResponse(getMockResponse());
1051 // need to set these separately so we can set the title bar icon later
1052 this.setOptionType(yesNoCancelOption);
1053 this.setMessageType(questionMessage);
1055 this.setInitialValue(initresponse);
1056 this.setOptions(options);
1057 this.setMessage(mainPanel);
1059 ourOptions = Arrays.asList(options);
1061 if (parentComponent != this)
1063 JInternalFrame jif = this.createInternalFrame(parentComponent, title);
1064 jif.setFrameIcon(null);
1065 jif.addInternalFrameListener(new InternalFrameListener()
1068 public void internalFrameActivated(InternalFrameEvent arg0)
1073 public void internalFrameClosed(InternalFrameEvent arg0)
1075 JvOptionPane.this.internalDialogHandleResponse();
1079 public void internalFrameClosing(InternalFrameEvent arg0)
1084 public void internalFrameDeactivated(InternalFrameEvent arg0)
1089 public void internalFrameDeiconified(InternalFrameEvent arg0)
1094 public void internalFrameIconified(InternalFrameEvent arg0)
1099 public void internalFrameOpened(InternalFrameEvent arg0)
1103 jif.setVisible(true);
1109 JDialog dialog = this.createDialog(parentComponent, title);
1110 dialog.setIconImages(ChannelProperties.getIconList());
1111 dialog.setVisible(true); // blocking
1112 this.internalDialogHandleResponse();
1117 private void internalDialogHandleResponse()
1119 String responseString = (String) this.getValue();
1120 int response = ourOptions.indexOf(responseString);
1122 if (!Platform.isJS())
1129 handleResponse(response);
1134 * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1135 * action) { callbacks.put(response, new Callable<Void>() {
1137 * @Override public Void call() { action.run(); return null; } }); return this;
1141 public JvOptionPane setResponseHandler(Object response,
1142 Callable<Void> action)
1144 callbacks.put(response, action);
1149 * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1152 public static int showDialogOnTop(String label, String actionString,
1153 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1155 if (!isInteractiveMode())
1157 return (int) getMockResponse();
1159 // Ensure Jalview window is brought to front (primarily for Quit
1160 // confirmation window to be visible)
1162 // This method of raising the Jalview window is broken in java
1163 // jalviewDesktop.setVisible(true);
1164 // jalviewDesktop.toFront();
1166 // A better hack which works is to create a new JFrame parent with
1167 // setAlwaysOnTop(true)
1168 JFrame dialogParent = new JFrame();
1169 dialogParent.setIconImages(ChannelProperties.getIconList());
1170 dialogParent.setAlwaysOnTop(true);
1172 int answer = JOptionPane.showConfirmDialog(dialogParent, label,
1173 actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
1175 dialogParent.setAlwaysOnTop(false);
1176 dialogParent.dispose();
1181 public void showDialogOnTopAsync(String label, String actionString,
1182 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1183 Object[] options, Object initialValue, boolean modal)
1185 JFrame frame = new JFrame();
1186 frame.setIconImages(ChannelProperties.getIconList());
1187 showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1188 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1191 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1192 String actionString, int JOPTIONPANE_OPTION,
1193 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1194 Object initialValue, boolean modal)
1196 showDialogOnTopAsync(dialogParent, label, actionString,
1197 JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1198 initialValue, modal, null);
1201 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1202 String actionString, int JOPTIONPANE_OPTION,
1203 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1204 Object initialValue, boolean modal, JButton[] buttons)
1206 if (!isInteractiveMode())
1208 handleResponse(getMockResponse());
1211 // Ensure Jalview window is brought to front (primarily for Quit
1212 // confirmation window to be visible)
1214 // This method of raising the Jalview window is broken in java
1215 // jalviewDesktop.setVisible(true);
1216 // jalviewDesktop.toFront();
1218 // A better hack which works is to create a new JFrame parent with
1219 // setAlwaysOnTop(true)
1220 dialogParent.setAlwaysOnTop(true);
1221 parentComponent = dialogParent;
1223 showDialog(label, actionString, JOPTIONPANE_OPTION,
1224 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1227 dialogParent.setAlwaysOnTop(false);
1228 dialogParent.dispose();
1232 * JalviewJS signals option selection by a property change event for the
1233 * option e.g. "OK". This methods responds to that by running the response
1234 * action that corresponds to that option.
1239 public void propertyChange(PropertyChangeEvent evt)
1241 Object newValue = evt.getNewValue();
1242 int ourOption = ourOptions.indexOf(newValue);
1245 handleResponse(ourOption);
1250 handleResponse(newValue);
1255 public void handleResponse(Object response)
1258 * this test is for NaN in Chrome
1260 if (response != null && !response.equals(response))
1264 Callable<Void> action = callbacks.get(response);
1270 } catch (Exception e)
1272 e.printStackTrace();
1274 if (parentComponent != null)
1275 parentComponent.requestFocus();
1280 * Create a non-modal confirm dialog
1282 public JDialog createDialog(Component parentComponent, Object message,
1283 String title, int optionType, int messageType, Icon icon,
1284 Object[] options, Object initialValue, boolean modal)
1286 return createDialog(parentComponent, message, title, optionType,
1287 messageType, icon, options, initialValue, modal, null);
1290 public JDialog createDialog(Component parentComponent, Object message,
1291 String title, int optionType, int messageType, Icon icon,
1292 Object[] options, Object initialValue, boolean modal,
1295 if (!isInteractiveMode())
1297 handleResponse(getMockResponse());
1300 JButton[] optionsButtons = null;
1301 Object initialValueButton = null;
1302 JOptionPane joptionpane = new JOptionPane();
1303 // Make button options
1304 int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1305 JOptionPane.CANCEL_OPTION };
1307 // we need the strings to make the buttons with actionEventListener
1308 if (options == null)
1310 ArrayList<String> options_default = new ArrayList<>();
1311 options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1312 if (optionType == JOptionPane.YES_NO_OPTION
1313 || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1315 options_default.add(UIManager.getString("OptionPane.noButtonText"));
1317 if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1320 .add(UIManager.getString("OptionPane.cancelButtonText"));
1322 options = options_default.toArray();
1324 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1327 if (((optionType == JOptionPane.YES_OPTION
1328 || optionType == JOptionPane.NO_OPTION
1329 || optionType == JOptionPane.CANCEL_OPTION
1330 || optionType == JOptionPane.OK_OPTION
1331 || optionType == JOptionPane.DEFAULT_OPTION)
1332 && options.length < 1)
1333 || ((optionType == JOptionPane.YES_NO_OPTION
1334 || optionType == JOptionPane.OK_CANCEL_OPTION)
1335 && options.length < 2)
1336 || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1337 && options.length < 3))
1340 .debug("JvOptionPane: not enough options for dialog type");
1342 optionsButtons = new JButton[options.length];
1343 for (int i = 0; i < options.length && i < 3; i++)
1345 Object o = options[i];
1346 int buttonAction = buttonActions[i];
1347 Callable<Void> action = callbacks.get(buttonAction);
1349 if (buttons != null && buttons.length > i && buttons[i] != null)
1357 jb.setText((String) o);
1358 jb.addActionListener(new ActionListener()
1361 public void actionPerformed(ActionEvent e)
1363 joptionpane.setValue(buttonAction);
1365 Executors.newSingleThreadExecutor().submit(action);
1366 // joptionpane.transferFocusBackward();
1367 joptionpane.transferFocusBackward();
1368 joptionpane.setVisible(false);
1369 // put focus and raise parent window if possible, unless cancel
1371 boolean raiseParent = (parentComponent != null);
1372 if (buttonAction == JOptionPane.CANCEL_OPTION)
1373 raiseParent = false;
1374 if (optionType == JOptionPane.YES_NO_OPTION
1375 && buttonAction == JOptionPane.NO_OPTION)
1376 raiseParent = false;
1379 parentComponent.requestFocus();
1380 if (parentComponent instanceof JInternalFrame)
1382 JInternalFrame jif = (JInternalFrame) parentComponent;
1387 else if (parentComponent instanceof Window)
1389 Window w = (Window) parentComponent;
1394 joptionpane.setVisible(false);
1397 optionsButtons[i] = jb;
1398 if (o.equals(initialValue))
1399 initialValueButton = jb;
1402 joptionpane.setMessage(message);
1403 joptionpane.setMessageType(messageType);
1404 joptionpane.setOptionType(optionType);
1405 joptionpane.setIcon(icon);
1406 joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1407 joptionpane.setInitialValue(
1408 Platform.isJS() ? initialValue : initialValueButton);
1410 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1411 dialog.setIconImages(ChannelProperties.getIconList());
1412 dialog.setModalityType(
1413 modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1414 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1419 * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1421 * returns true if button was found
1423 public static boolean clickButton(JFrame frame, int buttonType)
1430 * This helper method makes the JInternalFrame wait until it is notified by an
1431 * InternalFrameClosing event. This method also adds the given JOptionPane to
1432 * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1436 * The JInternalFrame to make modal.
1438 private static void startModal(JInternalFrame f)
1440 // We need to add an additional glasspane-like component directly
1441 // below the frame, which intercepts all mouse events that are not
1442 // directed at the frame itself.
1443 JPanel modalInterceptor = new JPanel();
1444 modalInterceptor.setOpaque(false);
1445 JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1446 lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1447 modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1448 modalInterceptor.addMouseListener(new MouseAdapter()
1451 modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1454 lp.add(modalInterceptor);
1457 // We need to explicitly dispatch events when we are blocking the event
1459 EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1462 while (!f.isClosed())
1464 if (EventQueue.isDispatchThread())
1466 // The getNextEventMethod() issues wait() when no
1467 // event is available, so we don't need do explicitly wait().
1468 AWTEvent ev = queue.getNextEvent();
1469 // This mimics EventQueue.dispatchEvent(). We can't use
1470 // EventQueue.dispatchEvent() directly, because it is
1471 // protected, unfortunately.
1472 if (ev instanceof ActiveEvent)
1473 ((ActiveEvent) ev).dispatch();
1474 else if (ev.getSource() instanceof Component)
1475 ((Component) ev.getSource()).dispatchEvent(ev);
1476 else if (ev.getSource() instanceof MenuComponent)
1477 ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1478 // Other events are ignored as per spec in
1479 // EventQueue.dispatchEvent
1483 // Give other threads a chance to become active.
1487 } catch (InterruptedException ex)
1489 // If we get interrupted, then leave the modal state.
1492 // Clean up the modal interceptor.
1493 lp.remove(modalInterceptor);
1495 // Remove the internal frame from its parent, so it is no longer
1496 // lurking around and clogging memory.
1497 Container parent = f.getParent();