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.ExecutorService;
46 import java.util.concurrent.Executors;
48 import javax.swing.Icon;
49 import javax.swing.JButton;
50 import javax.swing.JDialog;
51 import javax.swing.JFrame;
52 import javax.swing.JInternalFrame;
53 import javax.swing.JLayeredPane;
54 import javax.swing.JOptionPane;
55 import javax.swing.JPanel;
56 import javax.swing.SwingUtilities;
57 import javax.swing.UIManager;
58 import javax.swing.event.InternalFrameEvent;
59 import javax.swing.event.InternalFrameListener;
61 import jalview.bin.Console;
62 import jalview.util.ChannelProperties;
63 import jalview.util.MessageManager;
64 import jalview.util.Platform;
65 import jalview.util.dialogrunner.DialogRunnerI;
67 public class JvOptionPane extends JOptionPane
68 implements DialogRunnerI, PropertyChangeListener
70 private static final long serialVersionUID = -3019167117756785229L;
72 private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
74 private static boolean interactiveMode = true;
76 public static final Callable<Void> NULLCALLABLE = () -> {
80 private Component parentComponent;
82 private ExecutorService executor = Executors.newCachedThreadPool();
84 private JDialog dialog = null;
86 private Map<Object, Callable<Void>> callbacks = new HashMap<>();
89 * JalviewJS reports user choice in the dialog as the selected option (text);
90 * this list allows conversion to index (int)
92 List<Object> ourOptions;
94 public JvOptionPane(final Component parent)
96 this.parentComponent = Platform.isJS() ? this : parent;
100 public static int showConfirmDialog(Component parentComponent,
101 Object message) throws HeadlessException
103 // only called by test
104 return isInteractiveMode()
105 ? JOptionPane.showConfirmDialog(parentComponent, message)
106 : (int) getMockResponse();
110 * Message, title, optionType
112 * @param parentComponent
117 * @throws HeadlessException
119 public static int showConfirmDialog(Component parentComponent,
120 Object message, String title, int optionType)
121 throws HeadlessException
123 if (!isInteractiveMode())
125 return (int) getMockResponse();
129 case JvOptionPane.YES_NO_CANCEL_OPTION:
130 // FeatureRenderer amendFeatures ?? TODO ??
135 case JvOptionPane.YES_NO_OPTION:
136 // PromptUserConfig usage stats
137 // for now treated as "OK CANCEL"
139 case JvOptionPane.OK_CANCEL_OPTION:
140 // will fall back to simple HTML
141 return JOptionPane.showConfirmDialog(parentComponent, message, title,
147 * Adds a message type. Fallback is to just add it in the beginning.
149 * @param parentComponent
155 * @throws HeadlessException
157 public static int showConfirmDialog(Component parentComponent,
158 Object message, String title, int optionType, int messageType)
159 throws HeadlessException
161 // JalviewServicesChanged
162 // PromptUserConfig raiseDialog
163 return isInteractiveMode()
164 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
165 optionType, messageType)
166 : (int) getMockResponse();
172 * @param parentComponent
179 * @throws HeadlessException
181 public static int showConfirmDialog(Component parentComponent,
182 Object message, String title, int optionType, int messageType,
183 Icon icon) throws HeadlessException
185 // JvOptionPaneTest only
186 return isInteractiveMode()
187 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
188 optionType, messageType, icon)
189 : (int) getMockResponse();
193 * Internal version "OK"
195 * @param parentComponent
199 public static int showInternalConfirmDialog(Component parentComponent,
202 // JvOptionPaneTest only;
203 return isInteractiveMode()
204 ? JOptionPane.showInternalConfirmDialog(parentComponent,
206 : (int) getMockResponse();
210 * Internal version -- changed to standard version for now
212 * @param parentComponent
218 public static int showInternalConfirmDialog(Component parentComponent,
219 String message, String title, int optionType)
221 if (!isInteractiveMode())
223 return (int) getMockResponse();
227 case JvOptionPane.YES_NO_CANCEL_OPTION:
228 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
229 case JvOptionPane.YES_NO_OPTION:
230 // UserDefinedColoursSave -- relevant? TODO
233 case JvOptionPane.OK_CANCEL_OPTION:
235 // EditNameDialog --- uses panel for messsage TODO
237 // Desktop.inputURLMenuItem
239 return JOptionPane.showConfirmDialog(parentComponent, message, title,
246 * @param parentComponent
253 public static int showInternalConfirmDialog(Component parentComponent,
254 Object message, String title, int optionType, int messageType)
256 if (!isInteractiveMode())
258 return (int) getMockResponse();
262 case JvOptionPane.YES_NO_CANCEL_OPTION:
263 case JvOptionPane.YES_NO_OPTION:
264 // UserQuestionanaireCheck
268 case JvOptionPane.OK_CANCEL_OPTION:
269 // will fall back to simple HTML
270 return JOptionPane.showConfirmDialog(parentComponent, message, title,
271 optionType, messageType);
276 * adds icon; no longer internal
278 * @param parentComponent
286 public static int showInternalConfirmDialog(Component parentComponent,
287 Object message, String title, int optionType, int messageType,
290 if (!isInteractiveMode())
292 return (int) getMockResponse();
296 case JvOptionPane.YES_NO_CANCEL_OPTION:
297 case JvOptionPane.YES_NO_OPTION:
300 case JvOptionPane.OK_CANCEL_OPTION:
301 // Preferences editLink/newLink
302 return JOptionPane.showConfirmDialog(parentComponent, message, title,
303 optionType, messageType, icon);
309 * custom options full-featured
311 * @param parentComponent
318 * @param initialValue
320 * @throws HeadlessException
322 public static int showOptionDialog(Component parentComponent,
323 String message, String title, int optionType, int messageType,
324 Icon icon, Object[] options, Object initialValue)
325 throws HeadlessException
327 if (!isInteractiveMode())
329 return (int) getMockResponse();
335 // 1) AlignViewport for openLinkedAlignment
337 // Show a dialog with the option to open and link (cDNA <-> protein) as a
339 // alignment, either as a standalone alignment or in a split frame. Returns
340 // true if the new alignment was opened, false if not, because the user
341 // declined the offer.
343 // 2) UserDefinedColors warning about saving over a name already defined
345 return JOptionPane.showOptionDialog(parentComponent, message, title,
346 optionType, messageType, icon, options, initialValue);
353 * @throws HeadlessException
355 public static void showMessageDialog(Component parentComponent,
356 String message) throws HeadlessException
358 if (!isInteractiveMode())
360 outputMessage(message);
366 JOptionPane.showMessageDialog(parentComponent, message);
370 * OK with message, title, and type
372 * @param parentComponent
376 * @throws HeadlessException
378 public static void showMessageDialog(Component parentComponent,
379 String message, String title, int messageType)
380 throws HeadlessException
382 // 30 implementations -- all just fine.
384 if (!isInteractiveMode())
386 outputMessage(message);
390 JOptionPane.showMessageDialog(parentComponent,
391 getPrefix(messageType) + message, title, messageType);
395 * adds title and icon
397 * @param parentComponent
402 * @throws HeadlessException
404 public static void showMessageDialog(Component parentComponent,
405 String message, String title, int messageType, Icon icon)
406 throws HeadlessException
411 if (!isInteractiveMode())
413 outputMessage(message);
417 JOptionPane.showMessageDialog(parentComponent, message, title,
425 public static void showInternalMessageDialog(Component parentComponent,
429 // WsPreferences only
431 if (!isInteractiveMode())
433 outputMessage(message);
437 JOptionPane.showMessageDialog(parentComponent, message);
441 * Adds title and messageType
443 * @param parentComponent
448 public static void showInternalMessageDialog(Component parentComponent,
449 String message, String title, int messageType)
454 if (!isInteractiveMode())
456 outputMessage(message);
460 JOptionPane.showMessageDialog(parentComponent,
461 getPrefix(messageType) + message, title, messageType);
466 * @param parentComponent
472 public static void showInternalMessageDialog(Component parentComponent,
473 Object message, String title, int messageType, Icon icon)
478 if (!isInteractiveMode())
480 outputMessage(message);
484 JOptionPane.showMessageDialog(parentComponent, message, title,
492 * @throws HeadlessException
494 public static String showInputDialog(Object message)
495 throws HeadlessException
499 if (!isInteractiveMode())
501 return getMockResponse().toString();
504 return JOptionPane.showInputDialog(message);
508 * adds inital selection value
511 * @param initialSelectionValue
514 public static String showInputDialog(String message,
515 String initialSelectionValue)
517 if (!isInteractiveMode())
519 return getMockResponse().toString();
522 // AnnotationPanel character option
524 return JOptionPane.showInputDialog(message, initialSelectionValue);
528 * adds inital selection value
531 * @param initialSelectionValue
534 public static String showInputDialog(Object message,
535 Object initialSelectionValue)
537 if (!isInteractiveMode())
539 return getMockResponse().toString();
542 // AnnotationPanel character option
544 return JOptionPane.showInputDialog(message, initialSelectionValue);
550 * @param parentComponent
553 * @throws HeadlessException
555 public static String showInputDialog(Component parentComponent,
556 String message) throws HeadlessException
560 return isInteractiveMode()
561 ? JOptionPane.showInputDialog(parentComponent, message)
562 : getMockResponse().toString();
566 * input with initial selection
568 * @param parentComponent
570 * @param initialSelectionValue
573 public static String showInputDialog(Component parentComponent,
574 String message, String initialSelectionValue)
579 return isInteractiveMode()
580 ? JOptionPane.showInputDialog(parentComponent, message,
581 initialSelectionValue)
582 : getMockResponse().toString();
586 * input with initial selection
588 * @param parentComponent
590 * @param initialSelectionValue
593 public static String showInputDialog(Component parentComponent,
594 Object message, Object initialSelectionValue)
599 return isInteractiveMode()
600 ? JOptionPane.showInputDialog(parentComponent, message,
601 initialSelectionValue)
602 : getMockResponse().toString();
607 * @param parentComponent
612 * @throws HeadlessException
614 public static String showInputDialog(Component parentComponent,
615 String message, String title, int messageType)
616 throws HeadlessException
621 return isInteractiveMode()
622 ? JOptionPane.showInputDialog(parentComponent, message, title,
624 : getMockResponse().toString();
628 * Customized input option
630 * @param parentComponent
635 * @param selectionValues
636 * @param initialSelectionValue
638 * @throws HeadlessException
640 public static Object showInputDialog(Component parentComponent,
641 Object message, String title, int messageType, Icon icon,
642 Object[] selectionValues, Object initialSelectionValue)
643 throws HeadlessException
648 return isInteractiveMode()
649 ? JOptionPane.showInputDialog(parentComponent, message, title,
650 messageType, icon, selectionValues,
651 initialSelectionValue)
652 : getMockResponse().toString();
658 * @param parentComponent
662 public static String showInternalInputDialog(Component parentComponent,
667 return isInteractiveMode()
668 ? JOptionPane.showInternalInputDialog(parentComponent, message)
669 : getMockResponse().toString();
673 * internal with title and messageType
675 * @param parentComponent
681 public static String showInternalInputDialog(Component parentComponent,
682 String message, String title, int messageType)
685 // AlignFrame tabbedPane_mousePressed
687 return isInteractiveMode()
688 ? JOptionPane.showInternalInputDialog(parentComponent,
689 getPrefix(messageType) + message, title, messageType)
690 : getMockResponse().toString();
694 * customized internal
696 * @param parentComponent
701 * @param selectionValues
702 * @param initialSelectionValue
705 public static Object showInternalInputDialog(Component parentComponent,
706 String message, String title, int messageType, Icon icon,
707 Object[] selectionValues, Object initialSelectionValue)
711 return isInteractiveMode()
712 ? JOptionPane.showInternalInputDialog(parentComponent, message,
713 title, messageType, icon, selectionValues,
714 initialSelectionValue)
715 : getMockResponse().toString();
718 ///////////// end of options ///////////////
720 private static void outputMessage(Object message)
722 System.out.println(">>> JOption Message : " + message.toString());
725 public static Object getMockResponse()
730 public static void setMockResponse(Object mockOption)
732 JvOptionPane.mockResponse = mockOption;
735 public static void resetMock()
737 setMockResponse(JvOptionPane.CANCEL_OPTION);
738 setInteractiveMode(true);
741 public static boolean isInteractiveMode()
743 return interactiveMode;
746 public static void setInteractiveMode(boolean interactive)
748 JvOptionPane.interactiveMode = interactive;
751 private static String getPrefix(int messageType)
760 case JvOptionPane.WARNING_MESSAGE:
761 prefix = "WARNING! ";
763 case JvOptionPane.ERROR_MESSAGE:
774 * create a new option dialog that can be used to register responses - along
775 * lines of showOptionDialog
780 * @param defaultOption
781 * @param plainMessage
787 public static JvOptionPane newOptionDialog()
789 return new JvOptionPane(null);
792 public static JvOptionPane newOptionDialog(Component parentComponent)
794 return new JvOptionPane(parentComponent);
797 public void showDialog(String message, String title, int optionType,
798 int messageType, Icon icon, Object[] options, Object initialValue)
800 showDialog(message, title, optionType, messageType, icon, options,
804 public void showDialog(Object message, String title, int optionType,
805 int messageType, Icon icon, Object[] options, Object initialValue,
808 showDialog(message, title, optionType, messageType, icon, options,
809 initialValue, modal, null);
812 public void showDialog(Object message, String title, int optionType,
813 int messageType, Icon icon, Object[] options, Object initialValue,
814 boolean modal, JButton[] buttons)
816 if (!isInteractiveMode())
818 handleResponse(getMockResponse());
825 // 1) AlignViewport for openLinkedAlignment
827 // Show a dialog with the option to open and link (cDNA <-> protein) as a
829 // alignment, either as a standalone alignment or in a split frame. Returns
830 // true if the new alignment was opened, false if not, because the user
831 // declined the offer.
833 // 2) UserDefinedColors warning about saving over a name already defined
836 ourOptions = Arrays.asList(options);
840 boolean useButtons = false;
841 Object initialValueButton = null;
842 NOTNULL: if (buttons != null)
844 if (buttons.length != options.length)
846 jalview.bin.Console.error(
847 "Supplied buttons array not the same length as supplied options array.");
850 int[] buttonActions = { JOptionPane.YES_OPTION,
851 JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
852 for (int i = 0; i < options.length; i++)
854 Object o = options[i];
855 jalview.bin.Console.debug(
856 "Setting button " + i + " to '" + o.toString() + "'");
857 JButton jb = buttons[i];
859 if (o.equals(initialValue))
860 initialValueButton = jb;
862 int buttonAction = buttonActions[i];
863 Callable<Void> action = callbacks.get(buttonAction);
864 jb.setText((String) o);
865 jb.addActionListener(new ActionListener()
868 public void actionPerformed(ActionEvent e)
871 Object obj = e.getSource();
872 if (obj == null || !(obj instanceof Component))
874 jalview.bin.Console.debug(
875 "Could not find Component source of event object "
879 Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
880 JOptionPane.class, (Component) obj);
881 if (joptionpaneObject == null
882 || !(joptionpaneObject instanceof JOptionPane))
884 jalview.bin.Console.debug(
885 "Could not find JOptionPane ancestor of event object "
889 JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
890 joptionpane.setValue(buttonAction);
892 getExecutor().submit(action);
893 joptionpane.transferFocusBackward();
894 joptionpane.setVisible(false);
895 // put focus and raise parent window if possible, unless cancel or
897 boolean raiseParent = (parentComponent != null);
898 if (buttonAction == JOptionPane.CANCEL_OPTION)
900 if (optionType == JOptionPane.YES_NO_OPTION
901 && buttonAction == JOptionPane.NO_OPTION)
905 parentComponent.requestFocus();
906 if (parentComponent instanceof JInternalFrame)
908 JInternalFrame jif = (JInternalFrame) parentComponent;
913 else if (parentComponent instanceof Window)
915 Window w = (Window) parentComponent;
920 joptionpane.setVisible(false);
927 // use a JOptionPane as usual
928 int response = JOptionPane.showOptionDialog(parentComponent, message,
929 title, optionType, messageType, icon,
930 useButtons ? buttons : options,
931 useButtons ? initialValueButton : initialValue);
934 * In Java, the response is returned to this thread and handled here; (for
935 * Javascript, see propertyChange)
937 if (!Platform.isJS())
944 handleResponse(response);
950 * This is java similar to the swingjs handling, with the callbacks attached to
951 * the button press of the dialog. This means we can use a non-modal JDialog for
952 * the confirmation without blocking the GUI.
954 JOptionPane joptionpane = new JOptionPane();
955 // Make button options
956 int[] buttonActions = { JvOptionPane.YES_OPTION,
957 JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
959 // we need the strings to make the buttons with actionEventListener
962 ArrayList<String> options_default = new ArrayList<>();
964 .add(UIManager.getString("OptionPane.yesButtonText"));
965 if (optionType == JvOptionPane.YES_NO_OPTION
966 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
969 .add(UIManager.getString("OptionPane.noButtonText"));
971 if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
974 .add(UIManager.getString("OptionPane.cancelButtonText"));
976 options = options_default.toArray();
979 ArrayList<JButton> options_btns = new ArrayList<>();
980 Object initialValue_btn = null;
981 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
984 for (int i = 0; i < options.length && i < 3; i++)
986 Object o = options[i];
987 int buttonAction = buttonActions[i];
988 Callable<Void> action = callbacks.get(buttonAction);
989 JButton jb = new JButton();
990 jb.setText((String) o);
991 jb.addActionListener(new ActionListener()
995 public void actionPerformed(ActionEvent e)
997 joptionpane.setValue(buttonAction);
999 getExecutor().submit(action);
1000 // joptionpane.transferFocusBackward();
1001 joptionpane.transferFocusBackward();
1002 joptionpane.setVisible(false);
1003 // put focus and raise parent window if possible, unless cancel
1005 boolean raiseParent = (parentComponent != null);
1006 if (buttonAction == JvOptionPane.CANCEL_OPTION)
1007 raiseParent = false;
1008 if (optionType == JvOptionPane.YES_NO_OPTION
1009 && buttonAction == JvOptionPane.NO_OPTION)
1010 raiseParent = false;
1013 parentComponent.requestFocus();
1014 if (parentComponent instanceof JInternalFrame)
1016 JInternalFrame jif = (JInternalFrame) parentComponent;
1021 else if (parentComponent instanceof Window)
1023 Window w = (Window) parentComponent;
1028 joptionpane.setVisible(false);
1031 options_btns.add(jb);
1032 if (o.equals(initialValue))
1033 initialValue_btn = jb;
1036 joptionpane.setMessage(message);
1037 joptionpane.setMessageType(messageType);
1038 joptionpane.setOptionType(optionType);
1039 joptionpane.setIcon(icon);
1040 joptionpane.setOptions(
1041 Platform.isJS() ? options : options_btns.toArray());
1042 joptionpane.setInitialValue(
1043 Platform.isJS() ? initialValue : initialValue_btn);
1045 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1046 dialog.setIconImages(ChannelProperties.getIconList());
1047 dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1048 : ModalityType.MODELESS);
1049 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1050 dialog.setVisible(true);
1055 public void showInternalDialog(Object mainPanel, String title,
1056 int yesNoCancelOption, int questionMessage, Icon icon,
1057 Object[] options, String initresponse)
1059 if (!isInteractiveMode())
1061 handleResponse(getMockResponse());
1064 // need to set these separately so we can set the title bar icon later
1065 this.setOptionType(yesNoCancelOption);
1066 this.setMessageType(questionMessage);
1068 this.setInitialValue(initresponse);
1069 this.setOptions(options);
1070 this.setMessage(mainPanel);
1072 ourOptions = Arrays.asList(options);
1073 if (parentComponent != this
1074 && !(parentComponent == null && Desktop.instance == null))
1076 JInternalFrame jif = this.createInternalFrame(
1077 parentComponent != null ? parentComponent : Desktop.instance,
1079 jif.setFrameIcon(null);
1080 jif.addInternalFrameListener(new InternalFrameListener()
1083 public void internalFrameActivated(InternalFrameEvent arg0)
1088 public void internalFrameClosed(InternalFrameEvent arg0)
1090 JvOptionPane.this.internalDialogHandleResponse();
1094 public void internalFrameClosing(InternalFrameEvent arg0)
1099 public void internalFrameDeactivated(InternalFrameEvent arg0)
1104 public void internalFrameDeiconified(InternalFrameEvent arg0)
1109 public void internalFrameIconified(InternalFrameEvent arg0)
1114 public void internalFrameOpened(InternalFrameEvent arg0)
1118 jif.setVisible(true);
1124 JDialog dialog = this.createDialog(parentComponent, title);
1125 dialog.setIconImages(ChannelProperties.getIconList());
1126 dialog.setVisible(true); // blocking
1127 this.internalDialogHandleResponse();
1132 private void internalDialogHandleResponse()
1134 String responseString = (String) this.getValue();
1135 int response = ourOptions.indexOf(responseString);
1137 if (!Platform.isJS())
1144 handleResponse(response);
1149 * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1150 * action) { callbacks.put(response, new Callable<Void>() {
1152 * @Override public Void call() { action.run(); return null; } }); return this;
1156 public JvOptionPane setResponseHandler(Object response,
1157 Callable<Void> action)
1161 action = NULLCALLABLE;
1163 callbacks.put(response, action);
1167 public ExecutorService getExecutor()
1169 if (executor == null)
1170 executor = Executors.newCachedThreadPool();
1174 public void setExecutor(ExecutorService es)
1179 public void setDialog(JDialog d)
1184 public JDialog getDialog()
1190 * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1193 public static int showDialogOnTop(String label, String actionString,
1194 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1196 return showDialogOnTop(null, label, actionString, JOPTIONPANE_OPTION,
1197 JOPTIONPANE_MESSAGETYPE);
1200 public static int showDialogOnTop(Component dialogParentComponent,
1201 String label, String actionString, int JOPTIONPANE_OPTION,
1202 int JOPTIONPANE_MESSAGETYPE)
1204 if (!isInteractiveMode())
1206 return (int) getMockResponse();
1208 // Ensure Jalview window is brought to front (primarily for Quit
1209 // confirmation window to be visible)
1211 // This method of raising the Jalview window is broken in java
1212 // jalviewDesktop.setVisible(true);
1213 // jalviewDesktop.toFront();
1215 // A better hack which works is to create a new JFrame parent with
1216 // setAlwaysOnTop(true)
1217 JFrame dialogParent = new JFrame();
1218 if (dialogParentComponent == null)
1220 dialogParent.setIconImages(ChannelProperties.getIconList());
1221 dialogParent.setAlwaysOnTop(true);
1224 int answer = JOptionPane.showConfirmDialog(
1225 dialogParentComponent == null ? dialogParent
1226 : dialogParentComponent,
1227 label, actionString, JOPTIONPANE_OPTION,
1228 JOPTIONPANE_MESSAGETYPE);
1230 if (dialogParentComponent == null)
1232 dialogParent.setAlwaysOnTop(false);
1233 dialogParent.dispose();
1239 public void showDialogOnTopAsync(String label, String actionString,
1240 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1241 Object[] options, Object initialValue, boolean modal)
1243 JFrame frame = new JFrame();
1244 frame.setIconImages(ChannelProperties.getIconList());
1245 showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1246 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1249 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1250 String actionString, int JOPTIONPANE_OPTION,
1251 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1252 Object initialValue, boolean modal)
1254 showDialogOnTopAsync(dialogParent, label, actionString,
1255 JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1256 initialValue, modal, null);
1259 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1260 String actionString, int JOPTIONPANE_OPTION,
1261 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1262 Object initialValue, boolean modal, JButton[] buttons)
1264 if (!isInteractiveMode())
1266 handleResponse(getMockResponse());
1269 // Ensure Jalview window is brought to front (primarily for Quit
1270 // confirmation window to be visible)
1272 // This method of raising the Jalview window is broken in java
1273 // jalviewDesktop.setVisible(true);
1274 // jalviewDesktop.toFront();
1276 // A better hack which works is to create a new JFrame parent with
1277 // setAlwaysOnTop(true)
1278 dialogParent.setAlwaysOnTop(true);
1279 parentComponent = dialogParent;
1281 showDialog(label, actionString, JOPTIONPANE_OPTION,
1282 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1285 dialogParent.setAlwaysOnTop(false);
1286 dialogParent.dispose();
1290 * JalviewJS signals option selection by a property change event for the
1291 * option e.g. "OK". This methods responds to that by running the response
1292 * action that corresponds to that option.
1297 public void propertyChange(PropertyChangeEvent evt)
1299 Object newValue = evt.getNewValue();
1300 int ourOption = ourOptions.indexOf(newValue);
1303 handleResponse(ourOption);
1308 handleResponse(newValue);
1313 public void handleResponse(Object response)
1316 * this test is for NaN in Chrome
1318 if (response != null && !response.equals(response))
1322 Callable<Void> action = callbacks.get(response);
1327 getExecutor().submit(action).get();
1329 } catch (Exception e)
1331 e.printStackTrace();
1333 if (parentComponent != null)
1334 parentComponent.requestFocus();
1339 * Create a non-modal confirm dialog
1341 public JDialog createDialog(Component parentComponent, Object message,
1342 String title, int optionType, int messageType, Icon icon,
1343 Object[] options, Object initialValue, boolean modal)
1345 return createDialog(parentComponent, message, title, optionType,
1346 messageType, icon, options, initialValue, modal, null);
1349 public JDialog createDialog(Component parentComponent, Object message,
1350 String title, int optionType, int messageType, Icon icon,
1351 Object[] options, Object initialValue, boolean modal,
1354 if (!isInteractiveMode())
1356 handleResponse(getMockResponse());
1359 JButton[] optionsButtons = null;
1360 Object initialValueButton = null;
1361 JOptionPane joptionpane = new JOptionPane();
1362 // Make button options
1363 int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1364 JOptionPane.CANCEL_OPTION };
1366 // we need the strings to make the buttons with actionEventListener
1367 if (options == null)
1369 ArrayList<String> options_default = new ArrayList<>();
1370 options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1371 if (optionType == JOptionPane.YES_NO_OPTION
1372 || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1374 options_default.add(UIManager.getString("OptionPane.noButtonText"));
1376 if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1379 .add(UIManager.getString("OptionPane.cancelButtonText"));
1381 options = options_default.toArray();
1383 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1386 if (((optionType == JOptionPane.YES_OPTION
1387 || optionType == JOptionPane.NO_OPTION
1388 || optionType == JOptionPane.CANCEL_OPTION
1389 || optionType == JOptionPane.OK_OPTION
1390 || optionType == JOptionPane.DEFAULT_OPTION)
1391 && options.length < 1)
1392 || ((optionType == JOptionPane.YES_NO_OPTION
1393 || optionType == JOptionPane.OK_CANCEL_OPTION)
1394 && options.length < 2)
1395 || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1396 && options.length < 3))
1399 .debug("JvOptionPane: not enough options for dialog type");
1401 optionsButtons = new JButton[options.length];
1402 for (int i = 0; i < options.length && i < 3; i++)
1404 Object o = options[i];
1405 int buttonAction = buttonActions[i];
1406 Callable<Void> action = callbacks.get(buttonAction);
1408 if (buttons != null && buttons.length > i && buttons[i] != null)
1416 jb.setText((String) o);
1417 jb.addActionListener(new ActionListener()
1420 public void actionPerformed(ActionEvent e)
1422 joptionpane.setValue(buttonAction);
1424 getExecutor().submit(action);
1425 // joptionpane.transferFocusBackward();
1426 joptionpane.transferFocusBackward();
1427 joptionpane.setVisible(false);
1428 // put focus and raise parent window if possible, unless cancel
1430 boolean raiseParent = (parentComponent != null);
1431 if (buttonAction == JOptionPane.CANCEL_OPTION)
1432 raiseParent = false;
1433 if (optionType == JOptionPane.YES_NO_OPTION
1434 && buttonAction == JOptionPane.NO_OPTION)
1435 raiseParent = false;
1438 parentComponent.requestFocus();
1439 if (parentComponent instanceof JInternalFrame)
1441 JInternalFrame jif = (JInternalFrame) parentComponent;
1446 else if (parentComponent instanceof Window)
1448 Window w = (Window) parentComponent;
1453 joptionpane.setVisible(false);
1456 optionsButtons[i] = jb;
1457 if (o.equals(initialValue))
1458 initialValueButton = jb;
1461 joptionpane.setMessage(message);
1462 joptionpane.setMessageType(messageType);
1463 joptionpane.setOptionType(optionType);
1464 joptionpane.setIcon(icon);
1465 joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1466 joptionpane.setInitialValue(
1467 Platform.isJS() ? initialValue : initialValueButton);
1469 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1470 dialog.setIconImages(ChannelProperties.getIconList());
1471 dialog.setModalityType(
1472 modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1473 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1479 * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1481 * returns true if button was found
1483 public static boolean clickButton(JFrame frame, int buttonType)
1490 * This helper method makes the JInternalFrame wait until it is notified by an
1491 * InternalFrameClosing event. This method also adds the given JOptionPane to
1492 * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1496 * The JInternalFrame to make modal.
1498 private static void startModal(JInternalFrame f)
1500 // We need to add an additional glasspane-like component directly
1501 // below the frame, which intercepts all mouse events that are not
1502 // directed at the frame itself.
1503 JPanel modalInterceptor = new JPanel();
1504 modalInterceptor.setOpaque(false);
1505 JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1506 lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1507 modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1508 modalInterceptor.addMouseListener(new MouseAdapter()
1511 modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1514 lp.add(modalInterceptor);
1517 // We need to explicitly dispatch events when we are blocking the event
1519 EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1522 while (!f.isClosed())
1524 if (EventQueue.isDispatchThread())
1526 // The getNextEventMethod() issues wait() when no
1527 // event is available, so we don't need do explicitly wait().
1528 AWTEvent ev = queue.getNextEvent();
1529 // This mimics EventQueue.dispatchEvent(). We can't use
1530 // EventQueue.dispatchEvent() directly, because it is
1531 // protected, unfortunately.
1532 if (ev instanceof ActiveEvent)
1533 ((ActiveEvent) ev).dispatch();
1534 else if (ev.getSource() instanceof Component)
1535 ((Component) ev.getSource()).dispatchEvent(ev);
1536 else if (ev.getSource() instanceof MenuComponent)
1537 ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1538 // Other events are ignored as per spec in
1539 // EventQueue.dispatchEvent
1543 // Give other threads a chance to become active.
1547 } catch (InterruptedException ex)
1549 // If we get interrupted, then leave the modal state.
1552 // Clean up the modal interceptor.
1553 lp.remove(modalInterceptor);
1555 // Remove the internal frame from its parent, so it is no longer
1556 // lurking around and clogging memory.
1557 Container parent = f.getParent();
1563 public static JvOptionPane frameDialog(Object message, String title,
1564 int messageType, String[] buttonsTextS, String defaultButtonS,
1565 List<Callable<Void>> handlers, boolean modal)
1567 JFrame parent = new JFrame();
1568 JvOptionPane jvop = JvOptionPane.newOptionDialog();
1569 final String[] buttonsText;
1570 final String defaultButton;
1571 if (buttonsTextS == null)
1573 String ok = MessageManager.getString("action.ok");
1574 buttonsText = new String[] { ok };
1579 buttonsText = buttonsTextS;
1580 defaultButton = defaultButtonS;
1582 JButton[] buttons = new JButton[buttonsText.length];
1583 for (int i = 0; i < buttonsText.length; i++)
1585 buttons[i] = new JButton();
1586 buttons[i].setText(buttonsText[i]);
1587 Console.debug("DISABLING BUTTON " + buttons[i].getText());
1588 buttons[i].setEnabled(false);
1589 buttons[i].setVisible(false);
1592 int dialogType = -1;
1593 if (buttonsText.length == 1)
1595 dialogType = JOptionPane.OK_OPTION;
1597 else if (buttonsText.length == 2)
1599 dialogType = JOptionPane.YES_NO_OPTION;
1603 dialogType = JOptionPane.YES_NO_CANCEL_OPTION;
1605 jvop.setResponseHandler(JOptionPane.YES_OPTION,
1606 (handlers != null && handlers.size() > 0) ? handlers.get(0)
1608 if (dialogType == JOptionPane.YES_NO_OPTION
1609 || dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1611 jvop.setResponseHandler(JOptionPane.NO_OPTION,
1612 (handlers != null && handlers.size() > 1) ? handlers.get(1)
1615 if (dialogType == JOptionPane.YES_NO_CANCEL_OPTION)
1617 jvop.setResponseHandler(JOptionPane.CANCEL_OPTION,
1618 (handlers != null && handlers.size() > 2) ? handlers.get(2)
1622 final int dt = dialogType;
1623 jvop.getExecutor().execute(() -> {
1624 jvop.showDialog(message, title, dt, messageType, null, buttonsText,
1625 defaultButton, modal, buttons);