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.
24 import java.awt.AWTEvent;
25 import java.awt.ActiveEvent;
26 import java.awt.Component;
27 import java.awt.Container;
28 import java.awt.Dialog.ModalityType;
29 import java.awt.EventQueue;
30 import java.awt.HeadlessException;
31 import java.awt.MenuComponent;
32 import java.awt.Toolkit;
33 import java.awt.Window;
34 import java.awt.event.ActionEvent;
35 import java.awt.event.ActionListener;
36 import java.awt.event.MouseAdapter;
37 import java.awt.event.MouseMotionAdapter;
38 import java.beans.PropertyChangeEvent;
39 import java.beans.PropertyChangeListener;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.HashMap;
43 import java.util.List;
45 import java.util.concurrent.Callable;
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.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
79 * option (text); this list allows conversion to index (int)
81 List<Object> ourOptions;
83 public JvOptionPane(final Component parent)
85 this.parentComponent = Platform.isJS() ? this : parent;
88 public static int showConfirmDialog(Component parentComponent,
89 Object message) throws HeadlessException
91 // only called by test
92 return isInteractiveMode()
93 ? JOptionPane.showConfirmDialog(parentComponent, message)
94 : (int) getMockResponse();
98 * Message, title, optionType
100 * @param parentComponent
105 * @throws HeadlessException
107 public static int showConfirmDialog(Component parentComponent,
108 Object message, String title, int optionType)
109 throws HeadlessException
111 if (!isInteractiveMode())
113 return (int) getMockResponse();
117 case JvOptionPane.YES_NO_CANCEL_OPTION:
118 // FeatureRenderer amendFeatures ?? TODO ??
123 case JvOptionPane.YES_NO_OPTION:
124 // PromptUserConfig usage stats
125 // for now treated as "OK CANCEL"
127 case JvOptionPane.OK_CANCEL_OPTION:
128 // will fall back to simple HTML
129 return JOptionPane.showConfirmDialog(parentComponent, message, title,
135 * Adds a message type. Fallback is to just add it in the beginning.
137 * @param parentComponent
143 * @throws HeadlessException
145 public static int showConfirmDialog(Component parentComponent,
146 Object message, String title, int optionType, int messageType)
147 throws HeadlessException
149 // JalviewServicesChanged
150 // PromptUserConfig raiseDialog
151 return isInteractiveMode()
152 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
153 optionType, messageType)
154 : (int) getMockResponse();
160 * @param parentComponent
167 * @throws HeadlessException
169 public static int showConfirmDialog(Component parentComponent,
170 Object message, String title, int optionType, int messageType,
171 Icon icon) throws HeadlessException
173 // JvOptionPaneTest only
174 return isInteractiveMode()
175 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
176 optionType, messageType, icon)
177 : (int) getMockResponse();
181 * Internal version "OK"
183 * @param parentComponent
187 public static int showInternalConfirmDialog(Component parentComponent,
190 // JvOptionPaneTest only;
191 return isInteractiveMode()
192 ? JOptionPane.showInternalConfirmDialog(parentComponent,
194 : (int) getMockResponse();
198 * Internal version -- changed to standard version for now
200 * @param parentComponent
206 public static int showInternalConfirmDialog(Component parentComponent,
207 String message, String title, int optionType)
209 if (!isInteractiveMode())
211 return (int) getMockResponse();
215 case JvOptionPane.YES_NO_CANCEL_OPTION:
216 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
217 case JvOptionPane.YES_NO_OPTION:
218 // UserDefinedColoursSave -- relevant? TODO
221 case JvOptionPane.OK_CANCEL_OPTION:
223 // EditNameDialog --- uses panel for messsage TODO
225 // Desktop.inputURLMenuItem
227 return JOptionPane.showConfirmDialog(parentComponent, message, title,
234 * @param parentComponent
241 public static int showInternalConfirmDialog(Component parentComponent,
242 Object message, String title, int optionType, int messageType)
244 if (!isInteractiveMode())
246 return (int) getMockResponse();
250 case JvOptionPane.YES_NO_CANCEL_OPTION:
251 case JvOptionPane.YES_NO_OPTION:
252 // UserQuestionanaireCheck
256 case JvOptionPane.OK_CANCEL_OPTION:
257 // will fall back to simple HTML
258 return JOptionPane.showConfirmDialog(parentComponent, message, title,
259 optionType, messageType);
264 * adds icon; no longer internal
266 * @param parentComponent
274 public static int showInternalConfirmDialog(Component parentComponent,
275 Object message, String title, int optionType, int messageType,
278 if (!isInteractiveMode())
280 return (int) getMockResponse();
284 case JvOptionPane.YES_NO_CANCEL_OPTION:
285 case JvOptionPane.YES_NO_OPTION:
288 case JvOptionPane.OK_CANCEL_OPTION:
289 // Preferences editLink/newLink
290 return JOptionPane.showConfirmDialog(parentComponent, message, title,
291 optionType, messageType, icon);
297 * custom options full-featured
299 * @param parentComponent
306 * @param initialValue
308 * @throws HeadlessException
310 public static int showOptionDialog(Component parentComponent,
311 String message, String title, int optionType, int messageType,
312 Icon icon, Object[] options, Object initialValue)
313 throws HeadlessException
315 if (!isInteractiveMode())
317 return (int) getMockResponse();
323 // 1) AlignViewport for openLinkedAlignment
325 // Show a dialog with the option to open and link (cDNA <-> protein) as a
327 // alignment, either as a standalone alignment or in a split frame. Returns
328 // true if the new alignment was opened, false if not, because the user
329 // declined the offer.
331 // 2) UserDefinedColors warning about saving over a name already defined
333 return JOptionPane.showOptionDialog(parentComponent, message, title,
334 optionType, messageType, icon, options, initialValue);
341 * @throws HeadlessException
343 public static void showMessageDialog(Component parentComponent,
344 String message) throws HeadlessException
346 if (!isInteractiveMode())
348 outputMessage(message);
354 JOptionPane.showMessageDialog(parentComponent, message);
358 * OK with message, title, and type
360 * @param parentComponent
364 * @throws HeadlessException
366 public static void showMessageDialog(Component parentComponent,
367 String message, String title, int messageType)
368 throws HeadlessException
370 // 30 implementations -- all just fine.
372 if (!isInteractiveMode())
374 outputMessage(message);
378 JOptionPane.showMessageDialog(parentComponent,
379 getPrefix(messageType) + message, title, messageType);
383 * adds title and icon
385 * @param parentComponent
390 * @throws HeadlessException
392 public static void showMessageDialog(Component parentComponent,
393 String message, String title, int messageType, Icon icon)
394 throws HeadlessException
399 if (!isInteractiveMode())
401 outputMessage(message);
405 JOptionPane.showMessageDialog(parentComponent, message, title,
413 public static void showInternalMessageDialog(Component parentComponent,
417 // WsPreferences only
419 if (!isInteractiveMode())
421 outputMessage(message);
425 JOptionPane.showMessageDialog(parentComponent, message);
429 * Adds title and messageType
431 * @param parentComponent
436 public static void showInternalMessageDialog(Component parentComponent,
437 String message, String title, int messageType)
442 if (!isInteractiveMode())
444 outputMessage(message);
448 JOptionPane.showMessageDialog(parentComponent,
449 getPrefix(messageType) + message, title, messageType);
454 * @param parentComponent
460 public static void showInternalMessageDialog(Component parentComponent,
461 Object message, String title, int messageType, Icon icon)
466 if (!isInteractiveMode())
468 outputMessage(message);
472 JOptionPane.showMessageDialog(parentComponent, message, title,
480 * @throws HeadlessException
482 public static String showInputDialog(Object message)
483 throws HeadlessException
487 if (!isInteractiveMode())
489 return getMockResponse().toString();
492 return JOptionPane.showInputDialog(message);
496 * adds inital selection value
499 * @param initialSelectionValue
502 public static String showInputDialog(String message,
503 String initialSelectionValue)
505 if (!isInteractiveMode())
507 return getMockResponse().toString();
510 // AnnotationPanel character option
512 return JOptionPane.showInputDialog(message, initialSelectionValue);
516 * adds inital selection value
519 * @param initialSelectionValue
522 public static String showInputDialog(Object message,
523 Object initialSelectionValue)
525 if (!isInteractiveMode())
527 return getMockResponse().toString();
530 // AnnotationPanel character option
532 return JOptionPane.showInputDialog(message, initialSelectionValue);
538 * @param parentComponent
541 * @throws HeadlessException
543 public static String showInputDialog(Component parentComponent,
544 String message) throws HeadlessException
548 return isInteractiveMode()
549 ? JOptionPane.showInputDialog(parentComponent, message)
550 : getMockResponse().toString();
554 * input with initial selection
556 * @param parentComponent
558 * @param initialSelectionValue
561 public static String showInputDialog(Component parentComponent,
562 String message, String initialSelectionValue)
567 return isInteractiveMode()
568 ? JOptionPane.showInputDialog(parentComponent, message,
569 initialSelectionValue)
570 : getMockResponse().toString();
574 * input with initial selection
576 * @param parentComponent
578 * @param initialSelectionValue
581 public static String showInputDialog(Component parentComponent,
582 Object message, Object initialSelectionValue)
587 return isInteractiveMode()
588 ? JOptionPane.showInputDialog(parentComponent, message,
589 initialSelectionValue)
590 : getMockResponse().toString();
595 * @param parentComponent
600 * @throws HeadlessException
602 public static String showInputDialog(Component parentComponent,
603 String message, String title, int messageType)
604 throws HeadlessException
609 return isInteractiveMode()
610 ? JOptionPane.showInputDialog(parentComponent, message, title,
612 : getMockResponse().toString();
616 * Customized input option
618 * @param parentComponent
623 * @param selectionValues
624 * @param initialSelectionValue
626 * @throws HeadlessException
628 public static Object showInputDialog(Component parentComponent,
629 Object message, String title, int messageType, Icon icon,
630 Object[] selectionValues, Object initialSelectionValue)
631 throws HeadlessException
636 return isInteractiveMode()
637 ? JOptionPane.showInputDialog(parentComponent, message, title,
638 messageType, icon, selectionValues,
639 initialSelectionValue)
640 : getMockResponse().toString();
646 * @param parentComponent
650 public static String showInternalInputDialog(Component parentComponent,
655 return isInteractiveMode()
656 ? JOptionPane.showInternalInputDialog(parentComponent, message)
657 : getMockResponse().toString();
661 * internal with title and messageType
663 * @param parentComponent
669 public static String showInternalInputDialog(Component parentComponent,
670 String message, String title, int messageType)
673 // AlignFrame tabbedPane_mousePressed
675 return isInteractiveMode()
676 ? JOptionPane.showInternalInputDialog(parentComponent,
677 getPrefix(messageType) + message, title, messageType)
678 : getMockResponse().toString();
682 * customized internal
684 * @param parentComponent
689 * @param selectionValues
690 * @param initialSelectionValue
693 public static Object showInternalInputDialog(Component parentComponent,
694 String message, String title, int messageType, Icon icon,
695 Object[] selectionValues, Object initialSelectionValue)
699 return isInteractiveMode()
700 ? JOptionPane.showInternalInputDialog(parentComponent, message,
701 title, messageType, icon, selectionValues,
702 initialSelectionValue)
703 : getMockResponse().toString();
706 ///////////// end of options ///////////////
708 private static void outputMessage(Object message)
710 System.out.println(">>> JOption Message : " + message.toString());
713 public static Object getMockResponse()
718 public static void setMockResponse(Object mockOption)
720 JvOptionPane.mockResponse = mockOption;
723 public static void resetMock()
725 setMockResponse(JvOptionPane.CANCEL_OPTION);
726 setInteractiveMode(true);
729 public static boolean isInteractiveMode()
731 return interactiveMode;
734 public static void setInteractiveMode(boolean interactive)
736 JvOptionPane.interactiveMode = interactive;
739 private static String getPrefix(int messageType)
748 case JvOptionPane.WARNING_MESSAGE:
749 prefix = "WARNING! ";
751 case JvOptionPane.ERROR_MESSAGE:
762 * create a new option dialog that can be used to register responses - along
763 * lines of showOptionDialog
768 * @param defaultOption
769 * @param plainMessage
775 public static JvOptionPane newOptionDialog()
777 return new JvOptionPane(null);
780 public static JvOptionPane newOptionDialog(Component parentComponent)
782 return new JvOptionPane(parentComponent);
785 public void showDialog(String message, String title, int optionType,
786 int messageType, Icon icon, Object[] options, Object initialValue)
788 showDialog(message, title, optionType, messageType, icon, options,
792 public void showDialog(Object message, String title, int optionType,
793 int messageType, Icon icon, Object[] options, Object initialValue,
796 showDialog(message, title, optionType, messageType, icon, options,
797 initialValue, modal, null);
800 public void showDialog(Object message, String title, int optionType,
801 int messageType, Icon icon, Object[] options, Object initialValue,
802 boolean modal, JButton[] buttons)
804 if (!isInteractiveMode())
806 handleResponse(getMockResponse());
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;
922 * (for Javascript, see propertyChange)
924 if (!Platform.isJS())
931 handleResponse(response);
937 * This is java similar to the swingjs handling, with the callbacks
938 * attached to the button press of the dialog. This means we can use
939 * a non-modal JDialog for 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 add them here
970 for (int i = 0; i < options.length && i < 3; i++)
972 Object o = options[i];
973 int buttonAction = buttonActions[i];
974 Runnable action = callbacks.get(buttonAction);
975 JButton jb = new JButton();
976 jb.setText((String) o);
977 jb.addActionListener(new ActionListener()
980 public void actionPerformed(ActionEvent e)
982 joptionpane.setValue(buttonAction);
984 Executors.defaultThreadFactory().newThread(action).start();
985 // joptionpane.transferFocusBackward();
986 joptionpane.transferFocusBackward();
987 joptionpane.setVisible(false);
988 // put focus and raise parent window if possible, unless cancel
990 boolean raiseParent = (parentComponent != null);
991 if (buttonAction == JvOptionPane.CANCEL_OPTION)
993 if (optionType == JvOptionPane.YES_NO_OPTION
994 && buttonAction == JvOptionPane.NO_OPTION)
998 parentComponent.requestFocus();
999 if (parentComponent instanceof JInternalFrame)
1001 JInternalFrame jif = (JInternalFrame) parentComponent;
1006 else if (parentComponent instanceof Window)
1008 Window w = (Window) parentComponent;
1013 joptionpane.setVisible(false);
1016 options_btns.add(jb);
1017 if (o.equals(initialValue))
1018 initialValue_btn = jb;
1021 joptionpane.setMessage(message);
1022 joptionpane.setMessageType(messageType);
1023 joptionpane.setOptionType(optionType);
1024 joptionpane.setIcon(icon);
1025 joptionpane.setOptions(
1026 Platform.isJS() ? options : options_btns.toArray());
1027 joptionpane.setInitialValue(
1028 Platform.isJS() ? initialValue : initialValue_btn);
1030 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1031 dialog.setIconImage(WindowIcons.logoIcon.getImage());
1032 dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
1033 : ModalityType.MODELESS);
1034 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1035 dialog.setVisible(true);
1039 public void showInternalDialog(JPanel mainPanel, String title,
1040 int yesNoCancelOption, int questionMessage, Icon icon,
1041 Object[] options, String initresponse)
1043 if (!isInteractiveMode())
1045 handleResponse(getMockResponse());
1048 // need to set these separately so we can set the title bar icon later
1049 this.setOptionType(yesNoCancelOption);
1050 this.setMessageType(questionMessage);
1052 this.setInitialValue(initresponse);
1053 this.setOptions(options);
1054 this.setMessage(mainPanel);
1056 ourOptions = Arrays.asList(options);
1058 if (parentComponent != this)
1060 JInternalFrame jif = this.createInternalFrame(parentComponent, title);
1061 jif.setFrameIcon(WindowIcons.logoIcon);
1062 jif.addInternalFrameListener(new InternalFrameListener()
1065 public void internalFrameActivated(InternalFrameEvent arg0)
1070 public void internalFrameClosed(InternalFrameEvent arg0)
1072 JvOptionPane.this.internalDialogHandleResponse();
1076 public void internalFrameClosing(InternalFrameEvent arg0)
1081 public void internalFrameDeactivated(InternalFrameEvent arg0)
1086 public void internalFrameDeiconified(InternalFrameEvent arg0)
1091 public void internalFrameIconified(InternalFrameEvent arg0)
1096 public void internalFrameOpened(InternalFrameEvent arg0)
1100 jif.setVisible(true);
1106 JDialog dialog = this.createDialog(parentComponent, title);
1107 dialog.setIconImage(WindowIcons.logoIcon.getImage());
1108 dialog.setVisible(true); // blocking
1109 this.internalDialogHandleResponse();
1114 private void internalDialogHandleResponse()
1116 String responseString = (String) this.getValue();
1117 int response = ourOptions.indexOf(responseString);
1119 if (!Platform.isJS())
1126 handleResponse(response);
1132 public JvOptionPane setResponseHandler(Object response, Runnable action)
1134 callbacks.put(response, new Callable<Void>()
1147 public JvOptionPane setResponseHandler(Object response,
1148 Callable<Void> action)
1150 callbacks.put(response, action);
1155 * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1158 public static int showDialogOnTop(String label, String actionString,
1159 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1161 // Ensure Jalview window is brought to front (primarily for Quit
1162 // confirmation window to be visible)
1164 // This method of raising the Jalview window is broken in java
1165 // jalviewDesktop.setVisible(true);
1166 // jalviewDesktop.toFront();
1168 // A better hack which works is to create a new JFrame parent with
1169 // setAlwaysOnTop(true)
1170 JFrame dialogParent = new JFrame();
1171 dialogParent.setAlwaysOnTop(true);
1173 int answer = JOptionPane.showConfirmDialog(dialogParent, label,
1174 actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
1176 dialogParent.setAlwaysOnTop(false);
1177 dialogParent.dispose();
1182 public void showDialogOnTopAsync(String label, String actionString,
1183 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1184 Object[] options, Object initialValue, boolean modal)
1186 showDialogOnTopAsync(new JFrame(), label, actionString,
1187 JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1188 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 // Ensure Jalview window is brought to front (primarily for Quit
1207 // confirmation window to be visible)
1209 // This method of raising the Jalview window is broken in java
1210 // jalviewDesktop.setVisible(true);
1211 // jalviewDesktop.toFront();
1213 // A better hack which works is to create a new JFrame parent with
1214 // setAlwaysOnTop(true)
1215 dialogParent.setAlwaysOnTop(true);
1216 parentComponent = dialogParent;
1218 showDialog(label, actionString, JOPTIONPANE_OPTION,
1219 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1222 dialogParent.setAlwaysOnTop(false);
1223 dialogParent.dispose();
1227 * JalviewJS signals option selection by a property change event for the
1228 * option e.g. "OK". This methods responds to that by running the response
1229 * action that corresponds to that option.
1234 public void propertyChange(PropertyChangeEvent evt)
1236 Object newValue = evt.getNewValue();
1237 int ourOption = ourOptions.indexOf(newValue);
1240 handleResponse(ourOption);
1245 handleResponse(newValue);
1250 public void handleResponse(Object response)
1253 * this test is for NaN in Chrome
1255 if (response != null && !response.equals(response))
1259 Callable<Void> action = callbacks.get(response);
1265 } catch (Exception e)
1267 e.printStackTrace();
1269 if (parentComponent != null)
1270 parentComponent.requestFocus();
1275 * Create a non-modal confirm dialog
1277 public JDialog createDialog(Component parentComponent, Object message,
1278 String title, int optionType, int messageType, Icon icon,
1279 Object[] options, Object initialValue, boolean modal)
1281 return createDialog(parentComponent, message, title, optionType,
1282 messageType, icon, options, initialValue, modal, null);
1285 public JDialog createDialog(Component parentComponent, Object message,
1286 String title, int optionType, int messageType, Icon icon,
1287 Object[] options, Object initialValue, boolean modal,
1290 JButton[] optionsButtons = null;
1291 Object initialValueButton = null;
1292 JOptionPane joptionpane = new JOptionPane();
1293 // Make button options
1294 int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1295 JOptionPane.CANCEL_OPTION };
1297 // we need the strings to make the buttons with actionEventListener
1298 if (options == null)
1300 ArrayList<String> options_default = new ArrayList<>();
1301 options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1302 if (optionType == JOptionPane.YES_NO_OPTION
1303 || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1305 options_default.add(UIManager.getString("OptionPane.noButtonText"));
1307 if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1310 .add(UIManager.getString("OptionPane.cancelButtonText"));
1312 options = options_default.toArray();
1314 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1317 if (((optionType == JOptionPane.YES_OPTION
1318 || optionType == JOptionPane.NO_OPTION
1319 || optionType == JOptionPane.CANCEL_OPTION
1320 || optionType == JOptionPane.OK_OPTION
1321 || optionType == JOptionPane.DEFAULT_OPTION)
1322 && options.length < 1)
1323 || ((optionType == JOptionPane.YES_NO_OPTION
1324 || optionType == JOptionPane.OK_CANCEL_OPTION)
1325 && options.length < 2)
1326 || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1327 && options.length < 3))
1330 .debug("JvOptionPane: not enough options for dialog type");
1332 optionsButtons = new JButton[options.length];
1333 for (int i = 0; i < options.length && i < 3; i++)
1335 Object o = options[i];
1336 int buttonAction = buttonActions[i];
1337 Callable<Void> action = callbacks.get(buttonAction);
1339 if (buttons != null && buttons.length > i && buttons[i] != null)
1347 jb.setText((String) o);
1348 jb.addActionListener(new ActionListener()
1351 public void actionPerformed(ActionEvent e)
1353 joptionpane.setValue(buttonAction);
1355 Executors.newSingleThreadExecutor().submit(action);
1356 // joptionpane.transferFocusBackward();
1357 joptionpane.transferFocusBackward();
1358 joptionpane.setVisible(false);
1359 // put focus and raise parent window if possible, unless cancel
1361 boolean raiseParent = (parentComponent != null);
1362 if (buttonAction == JOptionPane.CANCEL_OPTION)
1363 raiseParent = false;
1364 if (optionType == JOptionPane.YES_NO_OPTION
1365 && buttonAction == JOptionPane.NO_OPTION)
1366 raiseParent = false;
1369 parentComponent.requestFocus();
1370 if (parentComponent instanceof JInternalFrame)
1372 JInternalFrame jif = (JInternalFrame) parentComponent;
1377 else if (parentComponent instanceof Window)
1379 Window w = (Window) parentComponent;
1384 joptionpane.setVisible(false);
1387 optionsButtons[i] = jb;
1388 if (o.equals(initialValue))
1389 initialValueButton = jb;
1392 joptionpane.setMessage(message);
1393 joptionpane.setMessageType(messageType);
1394 joptionpane.setOptionType(optionType);
1395 joptionpane.setIcon(icon);
1396 joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1397 joptionpane.setInitialValue(
1398 Platform.isJS() ? initialValue : initialValueButton);
1400 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1401 dialog.setModalityType(
1402 modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1403 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1408 * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1410 * returns true if button was found
1412 public static boolean clickButton(JFrame frame, int buttonType)
1419 * This helper method makes the JInternalFrame wait until it is notified by an
1420 * InternalFrameClosing event. This method also adds the given JOptionPane to
1421 * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1425 * The JInternalFrame to make modal.
1427 private static void startModal(JInternalFrame f)
1429 // We need to add an additional glasspane-like component directly
1430 // below the frame, which intercepts all mouse events that are not
1431 // directed at the frame itself.
1432 JPanel modalInterceptor = new JPanel();
1433 modalInterceptor.setOpaque(false);
1434 JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1435 lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1436 modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1437 modalInterceptor.addMouseListener(new MouseAdapter()
1440 modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1443 lp.add(modalInterceptor);
1446 // We need to explicitly dispatch events when we are blocking the event
1448 EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1451 while (!f.isClosed())
1453 if (EventQueue.isDispatchThread())
1455 // The getNextEventMethod() issues wait() when no
1456 // event is available, so we don't need do explicitly wait().
1457 AWTEvent ev = queue.getNextEvent();
1458 // This mimics EventQueue.dispatchEvent(). We can't use
1459 // EventQueue.dispatchEvent() directly, because it is
1460 // protected, unfortunately.
1461 if (ev instanceof ActiveEvent)
1462 ((ActiveEvent) ev).dispatch();
1463 else if (ev.getSource() instanceof Component)
1464 ((Component) ev.getSource()).dispatchEvent(ev);
1465 else if (ev.getSource() instanceof MenuComponent)
1466 ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1467 // Other events are ignored as per spec in
1468 // EventQueue.dispatchEvent
1472 // Give other threads a chance to become active.
1476 } catch (InterruptedException ex)
1478 // If we get interrupted, then leave the modal state.
1481 // Clean up the modal interceptor.
1482 lp.remove(modalInterceptor);
1484 // Remove the internal frame from its parent, so it is no longer
1485 // lurking around and clogging memory.
1486 Container parent = f.getParent();