2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import java.awt.AWTEvent;
24 import java.awt.ActiveEvent;
25 import java.awt.Component;
26 import java.awt.Container;
27 import java.awt.Dialog.ModalityType;
28 import java.awt.EventQueue;
29 import java.awt.HeadlessException;
30 import java.awt.MenuComponent;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.MouseAdapter;
36 import java.awt.event.MouseMotionAdapter;
37 import java.beans.PropertyChangeEvent;
38 import java.beans.PropertyChangeListener;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.List;
44 import java.util.concurrent.Callable;
45 import java.util.concurrent.Executors;
47 import javax.swing.Icon;
48 import javax.swing.JButton;
49 import javax.swing.JDialog;
50 import javax.swing.JFrame;
51 import javax.swing.JInternalFrame;
52 import javax.swing.JLayeredPane;
53 import javax.swing.JOptionPane;
54 import javax.swing.JPanel;
55 import javax.swing.SwingUtilities;
56 import javax.swing.UIManager;
57 import javax.swing.event.InternalFrameEvent;
58 import javax.swing.event.InternalFrameListener;
60 import jalview.util.Platform;
61 import jalview.util.dialogrunner.DialogRunnerI;
63 public class JvOptionPane extends JOptionPane
64 implements DialogRunnerI, PropertyChangeListener
66 private static final long serialVersionUID = -3019167117756785229L;
68 private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
70 private static boolean interactiveMode = true;
72 private Component parentComponent;
74 private Map<Object, Callable<Void>> callbacks = new HashMap<>();
77 * JalviewJS reports user choice in the dialog as the selected option (text);
78 * this list allows conversion to index (int)
80 List<Object> ourOptions;
82 public JvOptionPane(final Component parent)
84 this.parentComponent = Platform.isJS() ? this : parent;
87 public static int showConfirmDialog(Component parentComponent,
88 Object message) throws HeadlessException
90 // only called by test
91 return isInteractiveMode()
92 ? JOptionPane.showConfirmDialog(parentComponent, message)
93 : (int) getMockResponse();
97 * Message, title, optionType
99 * @param parentComponent
104 * @throws HeadlessException
106 public static int showConfirmDialog(Component parentComponent,
107 Object message, String title, int optionType)
108 throws HeadlessException
110 if (!isInteractiveMode())
112 return (int) getMockResponse();
116 case JvOptionPane.YES_NO_CANCEL_OPTION:
117 // FeatureRenderer amendFeatures ?? TODO ??
122 case JvOptionPane.YES_NO_OPTION:
123 // PromptUserConfig usage stats
124 // for now treated as "OK CANCEL"
126 case JvOptionPane.OK_CANCEL_OPTION:
127 // will fall back to simple HTML
128 return JOptionPane.showConfirmDialog(parentComponent, message, title,
134 * Adds a message type. Fallback is to just add it in the beginning.
136 * @param parentComponent
142 * @throws HeadlessException
144 public static int showConfirmDialog(Component parentComponent,
145 Object message, String title, int optionType, int messageType)
146 throws HeadlessException
148 // JalviewServicesChanged
149 // PromptUserConfig raiseDialog
150 return isInteractiveMode()
151 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
152 optionType, messageType)
153 : (int) getMockResponse();
159 * @param parentComponent
166 * @throws HeadlessException
168 public static int showConfirmDialog(Component parentComponent,
169 Object message, String title, int optionType, int messageType,
170 Icon icon) throws HeadlessException
172 // JvOptionPaneTest only
173 return isInteractiveMode()
174 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
175 optionType, messageType, icon)
176 : (int) getMockResponse();
180 * Internal version "OK"
182 * @param parentComponent
186 public static int showInternalConfirmDialog(Component parentComponent,
189 // JvOptionPaneTest only;
190 return isInteractiveMode()
191 ? JOptionPane.showInternalConfirmDialog(parentComponent,
193 : (int) getMockResponse();
197 * Internal version -- changed to standard version for now
199 * @param parentComponent
205 public static int showInternalConfirmDialog(Component parentComponent,
206 String message, String title, int optionType)
208 if (!isInteractiveMode())
210 return (int) getMockResponse();
214 case JvOptionPane.YES_NO_CANCEL_OPTION:
215 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
216 case JvOptionPane.YES_NO_OPTION:
217 // UserDefinedColoursSave -- relevant? TODO
220 case JvOptionPane.OK_CANCEL_OPTION:
222 // EditNameDialog --- uses panel for messsage TODO
224 // Desktop.inputURLMenuItem
226 return JOptionPane.showConfirmDialog(parentComponent, message, title,
233 * @param parentComponent
240 public static int showInternalConfirmDialog(Component parentComponent,
241 Object message, String title, int optionType, int messageType)
243 if (!isInteractiveMode())
245 return (int) getMockResponse();
249 case JvOptionPane.YES_NO_CANCEL_OPTION:
250 case JvOptionPane.YES_NO_OPTION:
251 // UserQuestionanaireCheck
255 case JvOptionPane.OK_CANCEL_OPTION:
256 // will fall back to simple HTML
257 return JOptionPane.showConfirmDialog(parentComponent, message, title,
258 optionType, messageType);
263 * adds icon; no longer internal
265 * @param parentComponent
273 public static int showInternalConfirmDialog(Component parentComponent,
274 Object message, String title, int optionType, int messageType,
277 if (!isInteractiveMode())
279 return (int) getMockResponse();
283 case JvOptionPane.YES_NO_CANCEL_OPTION:
284 case JvOptionPane.YES_NO_OPTION:
287 case JvOptionPane.OK_CANCEL_OPTION:
288 // Preferences editLink/newLink
289 return JOptionPane.showConfirmDialog(parentComponent, message, title,
290 optionType, messageType, icon);
296 * custom options full-featured
298 * @param parentComponent
305 * @param initialValue
307 * @throws HeadlessException
309 public static int showOptionDialog(Component parentComponent,
310 String message, String title, int optionType, int messageType,
311 Icon icon, Object[] options, Object initialValue)
312 throws HeadlessException
314 if (!isInteractiveMode())
316 return (int) getMockResponse();
322 // 1) AlignViewport for openLinkedAlignment
324 // Show a dialog with the option to open and link (cDNA <-> protein) as a
326 // alignment, either as a standalone alignment or in a split frame. Returns
327 // true if the new alignment was opened, false if not, because the user
328 // declined the offer.
330 // 2) UserDefinedColors warning about saving over a name already defined
332 return JOptionPane.showOptionDialog(parentComponent, message, title,
333 optionType, messageType, icon, options, initialValue);
340 * @throws HeadlessException
342 public static void showMessageDialog(Component parentComponent,
343 String message) throws HeadlessException
345 if (!isInteractiveMode())
347 outputMessage(message);
353 JOptionPane.showMessageDialog(parentComponent, message);
357 * OK with message, title, and type
359 * @param parentComponent
363 * @throws HeadlessException
365 public static void showMessageDialog(Component parentComponent,
366 String message, String title, int messageType)
367 throws HeadlessException
369 // 30 implementations -- all just fine.
371 if (!isInteractiveMode())
373 outputMessage(message);
377 JOptionPane.showMessageDialog(parentComponent,
378 getPrefix(messageType) + message, title, messageType);
382 * adds title and icon
384 * @param parentComponent
389 * @throws HeadlessException
391 public static void showMessageDialog(Component parentComponent,
392 String message, String title, int messageType, Icon icon)
393 throws HeadlessException
398 if (!isInteractiveMode())
400 outputMessage(message);
404 JOptionPane.showMessageDialog(parentComponent, message, title,
412 public static void showInternalMessageDialog(Component parentComponent,
416 // WsPreferences only
418 if (!isInteractiveMode())
420 outputMessage(message);
424 JOptionPane.showMessageDialog(parentComponent, message);
428 * Adds title and messageType
430 * @param parentComponent
435 public static void showInternalMessageDialog(Component parentComponent,
436 String message, String title, int messageType)
441 if (!isInteractiveMode())
443 outputMessage(message);
447 JOptionPane.showMessageDialog(parentComponent,
448 getPrefix(messageType) + message, title, messageType);
453 * @param parentComponent
459 public static void showInternalMessageDialog(Component parentComponent,
460 Object message, String title, int messageType, Icon icon)
465 if (!isInteractiveMode())
467 outputMessage(message);
471 JOptionPane.showMessageDialog(parentComponent, message, title,
479 * @throws HeadlessException
481 public static String showInputDialog(Object message)
482 throws HeadlessException
486 if (!isInteractiveMode())
488 return getMockResponse().toString();
491 return JOptionPane.showInputDialog(message);
495 * adds inital selection value
498 * @param initialSelectionValue
501 public static String showInputDialog(String message,
502 String initialSelectionValue)
504 if (!isInteractiveMode())
506 return getMockResponse().toString();
509 // AnnotationPanel character option
511 return JOptionPane.showInputDialog(message, initialSelectionValue);
515 * adds inital selection value
518 * @param initialSelectionValue
521 public static String showInputDialog(Object message,
522 Object initialSelectionValue)
524 if (!isInteractiveMode())
526 return getMockResponse().toString();
529 // AnnotationPanel character option
531 return JOptionPane.showInputDialog(message, initialSelectionValue);
537 * @param parentComponent
540 * @throws HeadlessException
542 public static String showInputDialog(Component parentComponent,
543 String message) throws HeadlessException
547 return isInteractiveMode()
548 ? JOptionPane.showInputDialog(parentComponent, message)
549 : getMockResponse().toString();
553 * input with initial selection
555 * @param parentComponent
557 * @param initialSelectionValue
560 public static String showInputDialog(Component parentComponent,
561 String message, String initialSelectionValue)
566 return isInteractiveMode()
567 ? JOptionPane.showInputDialog(parentComponent, message,
568 initialSelectionValue)
569 : getMockResponse().toString();
573 * input with initial selection
575 * @param parentComponent
577 * @param initialSelectionValue
580 public static String showInputDialog(Component parentComponent,
581 Object message, Object initialSelectionValue)
586 return isInteractiveMode()
587 ? JOptionPane.showInputDialog(parentComponent, message,
588 initialSelectionValue)
589 : getMockResponse().toString();
594 * @param parentComponent
599 * @throws HeadlessException
601 public static String showInputDialog(Component parentComponent,
602 String message, String title, int messageType)
603 throws HeadlessException
608 return isInteractiveMode()
609 ? JOptionPane.showInputDialog(parentComponent, message, title,
611 : getMockResponse().toString();
615 * Customized input option
617 * @param parentComponent
622 * @param selectionValues
623 * @param initialSelectionValue
625 * @throws HeadlessException
627 public static Object showInputDialog(Component parentComponent,
628 Object message, String title, int messageType, Icon icon,
629 Object[] selectionValues, Object initialSelectionValue)
630 throws HeadlessException
635 return isInteractiveMode()
636 ? JOptionPane.showInputDialog(parentComponent, message, title,
637 messageType, icon, selectionValues,
638 initialSelectionValue)
639 : getMockResponse().toString();
645 * @param parentComponent
649 public static String showInternalInputDialog(Component parentComponent,
654 return isInteractiveMode()
655 ? JOptionPane.showInternalInputDialog(parentComponent, message)
656 : getMockResponse().toString();
660 * internal with title and messageType
662 * @param parentComponent
668 public static String showInternalInputDialog(Component parentComponent,
669 String message, String title, int messageType)
672 // AlignFrame tabbedPane_mousePressed
674 return isInteractiveMode()
675 ? JOptionPane.showInternalInputDialog(parentComponent,
676 getPrefix(messageType) + message, title, messageType)
677 : getMockResponse().toString();
681 * customized internal
683 * @param parentComponent
688 * @param selectionValues
689 * @param initialSelectionValue
692 public static Object showInternalInputDialog(Component parentComponent,
693 String message, String title, int messageType, Icon icon,
694 Object[] selectionValues, Object initialSelectionValue)
698 return isInteractiveMode()
699 ? JOptionPane.showInternalInputDialog(parentComponent, message,
700 title, messageType, icon, selectionValues,
701 initialSelectionValue)
702 : getMockResponse().toString();
705 ///////////// end of options ///////////////
707 private static void outputMessage(Object message)
709 System.out.println(">>> JOption Message : " + message.toString());
712 public static Object getMockResponse()
717 public static void setMockResponse(Object mockOption)
719 JvOptionPane.mockResponse = mockOption;
722 public static void resetMock()
724 setMockResponse(JvOptionPane.CANCEL_OPTION);
725 setInteractiveMode(true);
728 public static boolean isInteractiveMode()
730 return interactiveMode;
733 public static void setInteractiveMode(boolean interactive)
735 JvOptionPane.interactiveMode = interactive;
738 private static String getPrefix(int messageType)
747 case JvOptionPane.WARNING_MESSAGE:
748 prefix = "WARNING! ";
750 case JvOptionPane.ERROR_MESSAGE:
761 * create a new option dialog that can be used to register responses - along
762 * lines of showOptionDialog
767 * @param defaultOption
768 * @param plainMessage
774 public static JvOptionPane newOptionDialog()
776 return new JvOptionPane(null);
779 public static JvOptionPane newOptionDialog(Component parentComponent)
781 return new JvOptionPane(parentComponent);
784 public void showDialog(String message, String title, int optionType,
785 int messageType, Icon icon, Object[] options, Object initialValue)
787 showDialog(message, title, optionType, messageType, icon, options,
791 public void showDialog(Object message, String title, int optionType,
792 int messageType, Icon icon, Object[] options, Object initialValue,
795 showDialog(message, title, optionType, messageType, icon, options,
796 initialValue, modal, null);
799 public void showDialog(Object message, String title, int optionType,
800 int messageType, Icon icon, Object[] options, Object initialValue,
801 boolean modal, JButton[] buttons)
803 if (!isInteractiveMode())
805 handleResponse(getMockResponse());
811 // 1) AlignViewport for openLinkedAlignment
813 // Show a dialog with the option to open and link (cDNA <-> protein) as a
815 // alignment, either as a standalone alignment or in a split frame. Returns
816 // true if the new alignment was opened, false if not, because the user
817 // declined the offer.
819 // 2) UserDefinedColors warning about saving over a name already defined
822 ourOptions = Arrays.asList(options);
826 boolean useButtons = false;
827 Object initialValueButton = null;
828 NOTNULL: if (buttons != null)
830 if (buttons.length != options.length)
832 jalview.bin.Console.error(
833 "Supplied buttons array not the same length as supplied options array.");
836 int[] buttonActions = { JOptionPane.YES_OPTION,
837 JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION };
838 for (int i = 0; i < options.length; i++)
840 Object o = options[i];
841 jalview.bin.Console.debug(
842 "Setting button " + i + " to '" + o.toString() + "'");
843 JButton jb = buttons[i];
845 if (o.equals(initialValue))
846 initialValueButton = jb;
848 int buttonAction = buttonActions[i];
849 Callable<Void> action = callbacks.get(buttonAction);
850 jb.setText((String) o);
851 jb.addActionListener(new ActionListener()
854 public void actionPerformed(ActionEvent e)
857 Object obj = e.getSource();
858 if (obj == null || !(obj instanceof Component))
860 jalview.bin.Console.debug(
861 "Could not find Component source of event object "
865 Object joptionpaneObject = SwingUtilities.getAncestorOfClass(
866 JOptionPane.class, (Component) obj);
867 if (joptionpaneObject == null
868 || !(joptionpaneObject instanceof JOptionPane))
870 jalview.bin.Console.debug(
871 "Could not find JOptionPane ancestor of event object "
875 JOptionPane joptionpane = (JOptionPane) joptionpaneObject;
876 joptionpane.setValue(buttonAction);
878 Executors.newSingleThreadExecutor().submit(action);
879 joptionpane.transferFocusBackward();
880 joptionpane.setVisible(false);
881 // put focus and raise parent window if possible, unless cancel or
883 boolean raiseParent = (parentComponent != null);
884 if (buttonAction == JOptionPane.CANCEL_OPTION)
886 if (optionType == JOptionPane.YES_NO_OPTION
887 && buttonAction == JOptionPane.NO_OPTION)
891 parentComponent.requestFocus();
892 if (parentComponent instanceof JInternalFrame)
894 JInternalFrame jif = (JInternalFrame) parentComponent;
899 else if (parentComponent instanceof Window)
901 Window w = (Window) parentComponent;
906 joptionpane.setVisible(false);
913 // use a JOptionPane as usual
914 int response = JOptionPane.showOptionDialog(parentComponent, message,
915 title, optionType, messageType, icon,
916 useButtons ? buttons : options,
917 useButtons ? initialValueButton : initialValue);
920 * In Java, the response is returned to this thread and handled here; (for
921 * Javascript, see propertyChange)
923 if (!Platform.isJS())
930 handleResponse(response);
936 * This is java similar to the swingjs handling, with the callbacks attached to
937 * the button press of the dialog. This means we can use a non-modal JDialog for
938 * the confirmation without blocking the GUI.
940 JOptionPane joptionpane = new JOptionPane();
941 // Make button options
942 int[] buttonActions = { JvOptionPane.YES_OPTION,
943 JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
945 // we need the strings to make the buttons with actionEventListener
948 ArrayList<String> options_default = new ArrayList<>();
950 .add(UIManager.getString("OptionPane.yesButtonText"));
951 if (optionType == JvOptionPane.YES_NO_OPTION
952 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
955 .add(UIManager.getString("OptionPane.noButtonText"));
957 if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
960 .add(UIManager.getString("OptionPane.cancelButtonText"));
962 options = options_default.toArray();
965 ArrayList<JButton> options_btns = new ArrayList<>();
966 Object initialValue_btn = null;
967 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
970 for (int i = 0; i < options.length && i < 3; i++)
972 Object o = options[i];
973 int buttonAction = buttonActions[i];
974 Callable<Void> 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.newSingleThreadExecutor().submit(action);
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);
1131 * @Override public JvOptionPane setResponseHandler(Object response, Runnable
1132 * action) { callbacks.put(response, new Callable<Void>() {
1134 * @Override public Void call() { action.run(); return null; } }); return this;
1138 public JvOptionPane setResponseHandler(Object response,
1139 Callable<Void> action)
1141 callbacks.put(response, action);
1146 * showDialogOnTop will create a dialog that (attempts to) come to top of OS
1149 public static int showDialogOnTop(String label, String actionString,
1150 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE)
1152 // Ensure Jalview window is brought to front (primarily for Quit
1153 // confirmation window to be visible)
1155 // This method of raising the Jalview window is broken in java
1156 // jalviewDesktop.setVisible(true);
1157 // jalviewDesktop.toFront();
1159 // A better hack which works is to create a new JFrame parent with
1160 // setAlwaysOnTop(true)
1161 JFrame dialogParent = new JFrame();
1162 dialogParent.setIconImage(WindowIcons.logoIcon.getImage());
1163 dialogParent.setAlwaysOnTop(true);
1165 int answer = JOptionPane.showConfirmDialog(dialogParent, label,
1166 actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE);
1168 dialogParent.setAlwaysOnTop(false);
1169 dialogParent.dispose();
1174 public void showDialogOnTopAsync(String label, String actionString,
1175 int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon,
1176 Object[] options, Object initialValue, boolean modal)
1178 JFrame frame = new JFrame();
1179 frame.setIconImage(WindowIcons.logoIcon.getImage());
1180 showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION,
1181 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal);
1184 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1185 String actionString, int JOPTIONPANE_OPTION,
1186 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1187 Object initialValue, boolean modal)
1189 showDialogOnTopAsync(dialogParent, label, actionString,
1190 JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options,
1191 initialValue, modal, null);
1194 public void showDialogOnTopAsync(JFrame dialogParent, Object label,
1195 String actionString, int JOPTIONPANE_OPTION,
1196 int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options,
1197 Object initialValue, boolean modal, JButton[] buttons)
1199 // Ensure Jalview window is brought to front (primarily for Quit
1200 // confirmation window to be visible)
1202 // This method of raising the Jalview window is broken in java
1203 // jalviewDesktop.setVisible(true);
1204 // jalviewDesktop.toFront();
1206 // A better hack which works is to create a new JFrame parent with
1207 // setAlwaysOnTop(true)
1208 dialogParent.setAlwaysOnTop(true);
1209 parentComponent = dialogParent;
1211 showDialog(label, actionString, JOPTIONPANE_OPTION,
1212 JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal,
1215 dialogParent.setAlwaysOnTop(false);
1216 dialogParent.dispose();
1220 * JalviewJS signals option selection by a property change event for the
1221 * option e.g. "OK". This methods responds to that by running the response
1222 * action that corresponds to that option.
1227 public void propertyChange(PropertyChangeEvent evt)
1229 Object newValue = evt.getNewValue();
1230 int ourOption = ourOptions.indexOf(newValue);
1233 handleResponse(ourOption);
1238 handleResponse(newValue);
1243 public void handleResponse(Object response)
1246 * this test is for NaN in Chrome
1248 if (response != null && !response.equals(response))
1252 Callable<Void> action = callbacks.get(response);
1258 } catch (Exception e)
1260 e.printStackTrace();
1262 if (parentComponent != null)
1263 parentComponent.requestFocus();
1268 * Create a non-modal confirm dialog
1270 public JDialog createDialog(Component parentComponent, Object message,
1271 String title, int optionType, int messageType, Icon icon,
1272 Object[] options, Object initialValue, boolean modal)
1274 return createDialog(parentComponent, message, title, optionType,
1275 messageType, icon, options, initialValue, modal, null);
1278 public JDialog createDialog(Component parentComponent, Object message,
1279 String title, int optionType, int messageType, Icon icon,
1280 Object[] options, Object initialValue, boolean modal,
1283 JButton[] optionsButtons = null;
1284 Object initialValueButton = null;
1285 JOptionPane joptionpane = new JOptionPane();
1286 // Make button options
1287 int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
1288 JOptionPane.CANCEL_OPTION };
1290 // we need the strings to make the buttons with actionEventListener
1291 if (options == null)
1293 ArrayList<String> options_default = new ArrayList<>();
1294 options_default.add(UIManager.getString("OptionPane.yesButtonText"));
1295 if (optionType == JOptionPane.YES_NO_OPTION
1296 || optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1298 options_default.add(UIManager.getString("OptionPane.noButtonText"));
1300 if (optionType == JOptionPane.YES_NO_CANCEL_OPTION)
1303 .add(UIManager.getString("OptionPane.cancelButtonText"));
1305 options = options_default.toArray();
1307 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to
1310 if (((optionType == JOptionPane.YES_OPTION
1311 || optionType == JOptionPane.NO_OPTION
1312 || optionType == JOptionPane.CANCEL_OPTION
1313 || optionType == JOptionPane.OK_OPTION
1314 || optionType == JOptionPane.DEFAULT_OPTION)
1315 && options.length < 1)
1316 || ((optionType == JOptionPane.YES_NO_OPTION
1317 || optionType == JOptionPane.OK_CANCEL_OPTION)
1318 && options.length < 2)
1319 || (optionType == JOptionPane.YES_NO_CANCEL_OPTION
1320 && options.length < 3))
1323 .debug("JvOptionPane: not enough options for dialog type");
1325 optionsButtons = new JButton[options.length];
1326 for (int i = 0; i < options.length && i < 3; i++)
1328 Object o = options[i];
1329 int buttonAction = buttonActions[i];
1330 Callable<Void> action = callbacks.get(buttonAction);
1332 if (buttons != null && buttons.length > i && buttons[i] != null)
1340 jb.setText((String) o);
1341 jb.addActionListener(new ActionListener()
1344 public void actionPerformed(ActionEvent e)
1346 joptionpane.setValue(buttonAction);
1348 Executors.newSingleThreadExecutor().submit(action);
1349 // joptionpane.transferFocusBackward();
1350 joptionpane.transferFocusBackward();
1351 joptionpane.setVisible(false);
1352 // put focus and raise parent window if possible, unless cancel
1354 boolean raiseParent = (parentComponent != null);
1355 if (buttonAction == JOptionPane.CANCEL_OPTION)
1356 raiseParent = false;
1357 if (optionType == JOptionPane.YES_NO_OPTION
1358 && buttonAction == JOptionPane.NO_OPTION)
1359 raiseParent = false;
1362 parentComponent.requestFocus();
1363 if (parentComponent instanceof JInternalFrame)
1365 JInternalFrame jif = (JInternalFrame) parentComponent;
1370 else if (parentComponent instanceof Window)
1372 Window w = (Window) parentComponent;
1377 joptionpane.setVisible(false);
1380 optionsButtons[i] = jb;
1381 if (o.equals(initialValue))
1382 initialValueButton = jb;
1385 joptionpane.setMessage(message);
1386 joptionpane.setMessageType(messageType);
1387 joptionpane.setOptionType(optionType);
1388 joptionpane.setIcon(icon);
1389 joptionpane.setOptions(Platform.isJS() ? options : optionsButtons);
1390 joptionpane.setInitialValue(
1391 Platform.isJS() ? initialValue : initialValueButton);
1393 JDialog dialog = joptionpane.createDialog(parentComponent, title);
1394 dialog.setIconImage(WindowIcons.logoIcon.getImage());
1395 dialog.setModalityType(
1396 modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS);
1397 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
1402 * Utility to programmatically click a button on a JOptionPane (as a JFrame)
1404 * returns true if button was found
1406 public static boolean clickButton(JFrame frame, int buttonType)
1413 * This helper method makes the JInternalFrame wait until it is notified by an
1414 * InternalFrameClosing event. This method also adds the given JOptionPane to
1415 * the JInternalFrame and sizes it according to the JInternalFrame's preferred
1419 * The JInternalFrame to make modal.
1421 private static void startModal(JInternalFrame f)
1423 // We need to add an additional glasspane-like component directly
1424 // below the frame, which intercepts all mouse events that are not
1425 // directed at the frame itself.
1426 JPanel modalInterceptor = new JPanel();
1427 modalInterceptor.setOpaque(false);
1428 JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
1429 lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
1430 modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
1431 modalInterceptor.addMouseListener(new MouseAdapter()
1434 modalInterceptor.addMouseMotionListener(new MouseMotionAdapter()
1437 lp.add(modalInterceptor);
1440 // We need to explicitly dispatch events when we are blocking the event
1442 EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
1445 while (!f.isClosed())
1447 if (EventQueue.isDispatchThread())
1449 // The getNextEventMethod() issues wait() when no
1450 // event is available, so we don't need do explicitly wait().
1451 AWTEvent ev = queue.getNextEvent();
1452 // This mimics EventQueue.dispatchEvent(). We can't use
1453 // EventQueue.dispatchEvent() directly, because it is
1454 // protected, unfortunately.
1455 if (ev instanceof ActiveEvent)
1456 ((ActiveEvent) ev).dispatch();
1457 else if (ev.getSource() instanceof Component)
1458 ((Component) ev.getSource()).dispatchEvent(ev);
1459 else if (ev.getSource() instanceof MenuComponent)
1460 ((MenuComponent) ev.getSource()).dispatchEvent(ev);
1461 // Other events are ignored as per spec in
1462 // EventQueue.dispatchEvent
1466 // Give other threads a chance to become active.
1470 } catch (InterruptedException ex)
1472 // If we get interrupted, then leave the modal state.
1475 // Clean up the modal interceptor.
1476 lp.remove(modalInterceptor);
1478 // Remove the internal frame from its parent, so it is no longer
1479 // lurking around and clogging memory.
1480 Container parent = f.getParent();