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.Component;
25 import java.awt.Dialog.ModalityType;
26 import java.awt.HeadlessException;
27 import java.awt.Window;
28 import java.awt.event.ActionEvent;
29 import java.awt.event.ActionListener;
30 import java.beans.PropertyChangeEvent;
31 import java.beans.PropertyChangeListener;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.List;
37 import java.util.concurrent.Executors;
39 import javax.swing.Icon;
40 import javax.swing.JButton;
41 import javax.swing.JDialog;
42 import javax.swing.JInternalFrame;
43 import javax.swing.JOptionPane;
44 import javax.swing.JPanel;
45 import javax.swing.UIManager;
47 import jalview.util.Platform;
48 import jalview.util.dialogrunner.DialogRunnerI;
50 public class JvOptionPane extends JOptionPane
51 implements DialogRunnerI, PropertyChangeListener
53 private static final long serialVersionUID = -3019167117756785229L;
55 private static Object mockResponse = JvOptionPane.CANCEL_OPTION;
57 private static boolean interactiveMode = true;
59 private Component parentComponent;
61 private Map<Object, Runnable> callbacks = new HashMap<>();
64 * JalviewJS reports user choice in the dialog as the selected
65 * option (text); this list allows conversion to index (int)
67 List<Object> ourOptions;
69 public JvOptionPane(final Component parent)
71 this.parentComponent = Platform.isJS() ? this : parent;
74 public static int showConfirmDialog(Component parentComponent,
75 Object message) throws HeadlessException
77 // only called by test
78 return isInteractiveMode()
79 ? JOptionPane.showConfirmDialog(parentComponent, message)
80 : (int) getMockResponse();
84 * Message, title, optionType
86 * @param parentComponent
91 * @throws HeadlessException
93 public static int showConfirmDialog(Component parentComponent,
94 Object message, String title, int optionType)
95 throws HeadlessException
97 if (!isInteractiveMode())
99 return (int) getMockResponse();
103 case JvOptionPane.YES_NO_CANCEL_OPTION:
104 // FeatureRenderer amendFeatures ?? TODO ??
109 case JvOptionPane.YES_NO_OPTION:
110 // PromptUserConfig usage stats
111 // for now treated as "OK CANCEL"
113 case JvOptionPane.OK_CANCEL_OPTION:
114 // will fall back to simple HTML
115 return JOptionPane.showConfirmDialog(parentComponent, message, title,
121 * Adds a message type. Fallback is to just add it in the beginning.
123 * @param parentComponent
129 * @throws HeadlessException
131 public static int showConfirmDialog(Component parentComponent,
132 Object message, String title, int optionType, int messageType)
133 throws HeadlessException
135 // JalviewServicesChanged
136 // PromptUserConfig raiseDialog
137 return isInteractiveMode()
138 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
139 optionType, messageType)
140 : (int) getMockResponse();
146 * @param parentComponent
153 * @throws HeadlessException
155 public static int showConfirmDialog(Component parentComponent,
156 Object message, String title, int optionType, int messageType,
157 Icon icon) throws HeadlessException
159 // JvOptionPaneTest only
160 return isInteractiveMode()
161 ? JOptionPane.showConfirmDialog(parentComponent, message, title,
162 optionType, messageType, icon)
163 : (int) getMockResponse();
167 * Internal version "OK"
169 * @param parentComponent
173 public static int showInternalConfirmDialog(Component parentComponent,
176 // JvOptionPaneTest only;
177 return isInteractiveMode()
178 ? JOptionPane.showInternalConfirmDialog(parentComponent,
180 : (int) getMockResponse();
184 * Internal version -- changed to standard version for now
186 * @param parentComponent
192 public static int showInternalConfirmDialog(Component parentComponent,
193 String message, String title, int optionType)
195 if (!isInteractiveMode())
197 return (int) getMockResponse();
201 case JvOptionPane.YES_NO_CANCEL_OPTION:
202 // ColourMenuHelper.addMenuItmers.offerRemoval TODO
203 case JvOptionPane.YES_NO_OPTION:
204 // UserDefinedColoursSave -- relevant? TODO
207 case JvOptionPane.OK_CANCEL_OPTION:
209 // EditNameDialog --- uses panel for messsage TODO
211 // Desktop.inputURLMenuItem
213 return JOptionPane.showConfirmDialog(parentComponent, message, title,
220 * @param parentComponent
227 public static int showInternalConfirmDialog(Component parentComponent,
228 Object message, String title, int optionType, int messageType)
230 if (!isInteractiveMode())
232 return (int) getMockResponse();
236 case JvOptionPane.YES_NO_CANCEL_OPTION:
237 case JvOptionPane.YES_NO_OPTION:
238 // UserQuestionanaireCheck
242 case JvOptionPane.OK_CANCEL_OPTION:
243 // will fall back to simple HTML
244 return JOptionPane.showConfirmDialog(parentComponent, message, title,
245 optionType, messageType);
250 * adds icon; no longer internal
252 * @param parentComponent
260 public static int showInternalConfirmDialog(Component parentComponent,
261 Object message, String title, int optionType, int messageType,
264 if (!isInteractiveMode())
266 return (int) getMockResponse();
270 case JvOptionPane.YES_NO_CANCEL_OPTION:
271 case JvOptionPane.YES_NO_OPTION:
274 case JvOptionPane.OK_CANCEL_OPTION:
275 // Preferences editLink/newLink
276 return JOptionPane.showConfirmDialog(parentComponent, message, title,
277 optionType, messageType, icon);
283 * custom options full-featured
285 * @param parentComponent
292 * @param initialValue
294 * @throws HeadlessException
296 public static int showOptionDialog(Component parentComponent,
297 String message, String title, int optionType, int messageType,
298 Icon icon, Object[] options, Object initialValue)
299 throws HeadlessException
301 if (!isInteractiveMode())
303 return (int) getMockResponse();
309 // 1) AlignViewport for openLinkedAlignment
311 // Show a dialog with the option to open and link (cDNA <-> protein) as a
313 // alignment, either as a standalone alignment or in a split frame. Returns
314 // true if the new alignment was opened, false if not, because the user
315 // declined the offer.
317 // 2) UserDefinedColors warning about saving over a name already defined
319 return JOptionPane.showOptionDialog(parentComponent, message, title,
320 optionType, messageType, icon, options, initialValue);
327 * @throws HeadlessException
329 public static void showMessageDialog(Component parentComponent,
330 String message) throws HeadlessException
332 if (!isInteractiveMode())
334 outputMessage(message);
340 JOptionPane.showMessageDialog(parentComponent, message);
344 * OK with message, title, and type
346 * @param parentComponent
350 * @throws HeadlessException
352 public static void showMessageDialog(Component parentComponent,
353 String message, String title, int messageType)
354 throws HeadlessException
356 // 30 implementations -- all just fine.
358 if (!isInteractiveMode())
360 outputMessage(message);
364 JOptionPane.showMessageDialog(parentComponent,
365 getPrefix(messageType) + message, title, messageType);
369 * adds title and icon
371 * @param parentComponent
376 * @throws HeadlessException
378 public static void showMessageDialog(Component parentComponent,
379 String message, String title, int messageType, Icon icon)
380 throws HeadlessException
385 if (!isInteractiveMode())
387 outputMessage(message);
391 JOptionPane.showMessageDialog(parentComponent, message, title,
399 public static void showInternalMessageDialog(Component parentComponent,
403 // WsPreferences only
405 if (!isInteractiveMode())
407 outputMessage(message);
411 JOptionPane.showMessageDialog(parentComponent, message);
415 * Adds title and messageType
417 * @param parentComponent
422 public static void showInternalMessageDialog(Component parentComponent,
423 String message, String title, int messageType)
428 if (!isInteractiveMode())
430 outputMessage(message);
434 JOptionPane.showMessageDialog(parentComponent,
435 getPrefix(messageType) + message, title, messageType);
440 * @param parentComponent
446 public static void showInternalMessageDialog(Component parentComponent,
447 Object message, String title, int messageType, Icon icon)
452 if (!isInteractiveMode())
454 outputMessage(message);
458 JOptionPane.showMessageDialog(parentComponent, message, title,
466 * @throws HeadlessException
468 public static String showInputDialog(Object message)
469 throws HeadlessException
473 if (!isInteractiveMode())
475 return getMockResponse().toString();
478 return JOptionPane.showInputDialog(message);
482 * adds inital selection value
485 * @param initialSelectionValue
488 public static String showInputDialog(String message,
489 String initialSelectionValue)
491 if (!isInteractiveMode())
493 return getMockResponse().toString();
496 // AnnotationPanel character option
498 return JOptionPane.showInputDialog(message, initialSelectionValue);
502 * adds inital selection value
505 * @param initialSelectionValue
508 public static String showInputDialog(Object message,
509 Object initialSelectionValue)
511 if (!isInteractiveMode())
513 return getMockResponse().toString();
516 // AnnotationPanel character option
518 return JOptionPane.showInputDialog(message, initialSelectionValue);
524 * @param parentComponent
527 * @throws HeadlessException
529 public static String showInputDialog(Component parentComponent,
530 String message) throws HeadlessException
534 return isInteractiveMode()
535 ? JOptionPane.showInputDialog(parentComponent, message)
536 : getMockResponse().toString();
540 * input with initial selection
542 * @param parentComponent
544 * @param initialSelectionValue
547 public static String showInputDialog(Component parentComponent,
548 String message, String initialSelectionValue)
553 return isInteractiveMode()
554 ? JOptionPane.showInputDialog(parentComponent, message,
555 initialSelectionValue)
556 : getMockResponse().toString();
560 * input with initial selection
562 * @param parentComponent
564 * @param initialSelectionValue
567 public static String showInputDialog(Component parentComponent,
568 Object message, Object initialSelectionValue)
573 return isInteractiveMode()
574 ? JOptionPane.showInputDialog(parentComponent, message,
575 initialSelectionValue)
576 : getMockResponse().toString();
581 * @param parentComponent
586 * @throws HeadlessException
588 public static String showInputDialog(Component parentComponent,
589 String message, String title, int messageType)
590 throws HeadlessException
595 return isInteractiveMode()
596 ? JOptionPane.showInputDialog(parentComponent, message, title,
598 : getMockResponse().toString();
602 * Customized input option
604 * @param parentComponent
609 * @param selectionValues
610 * @param initialSelectionValue
612 * @throws HeadlessException
614 public static Object showInputDialog(Component parentComponent,
615 Object message, String title, int messageType, Icon icon,
616 Object[] selectionValues, Object initialSelectionValue)
617 throws HeadlessException
622 return isInteractiveMode()
623 ? JOptionPane.showInputDialog(parentComponent, message, title,
624 messageType, icon, selectionValues,
625 initialSelectionValue)
626 : getMockResponse().toString();
632 * @param parentComponent
636 public static String showInternalInputDialog(Component parentComponent,
641 return isInteractiveMode()
642 ? JOptionPane.showInternalInputDialog(parentComponent, message)
643 : getMockResponse().toString();
647 * internal with title and messageType
649 * @param parentComponent
655 public static String showInternalInputDialog(Component parentComponent,
656 String message, String title, int messageType)
659 // AlignFrame tabbedPane_mousePressed
661 return isInteractiveMode()
662 ? JOptionPane.showInternalInputDialog(parentComponent,
663 getPrefix(messageType) + message, title, messageType)
664 : getMockResponse().toString();
668 * customized internal
670 * @param parentComponent
675 * @param selectionValues
676 * @param initialSelectionValue
679 public static Object showInternalInputDialog(Component parentComponent,
680 String message, String title, int messageType, Icon icon,
681 Object[] selectionValues, Object initialSelectionValue)
685 return isInteractiveMode()
686 ? JOptionPane.showInternalInputDialog(parentComponent, message,
687 title, messageType, icon, selectionValues,
688 initialSelectionValue)
689 : getMockResponse().toString();
692 ///////////// end of options ///////////////
694 private static void outputMessage(Object message)
696 System.out.println(">>> JOption Message : " + message.toString());
699 public static Object getMockResponse()
704 public static void setMockResponse(Object mockOption)
706 JvOptionPane.mockResponse = mockOption;
709 public static void resetMock()
711 setMockResponse(JvOptionPane.CANCEL_OPTION);
712 setInteractiveMode(true);
715 public static boolean isInteractiveMode()
717 return interactiveMode;
720 public static void setInteractiveMode(boolean interactive)
722 JvOptionPane.interactiveMode = interactive;
725 private static String getPrefix(int messageType)
734 case JvOptionPane.WARNING_MESSAGE:
735 prefix = "WARNING! ";
737 case JvOptionPane.ERROR_MESSAGE:
748 * create a new option dialog that can be used to register responses - along
749 * lines of showOptionDialog
754 * @param defaultOption
755 * @param plainMessage
761 public static JvOptionPane newOptionDialog(Component parentComponent)
763 return new JvOptionPane(parentComponent);
766 public void showDialog(String message, String title, int optionType,
767 int messageType, Icon icon, Object[] options, Object initialValue)
769 showDialog(message, title, optionType, messageType, icon, options,
773 public void showDialog(String message, String title, int optionType,
774 int messageType, Icon icon, Object[] options, Object initialValue,
777 if (!isInteractiveMode())
779 handleResponse(getMockResponse());
785 // 1) AlignViewport for openLinkedAlignment
787 // Show a dialog with the option to open and link (cDNA <-> protein) as a
789 // alignment, either as a standalone alignment or in a split frame. Returns
790 // true if the new alignment was opened, false if not, because the user
791 // declined the offer.
793 // 2) UserDefinedColors warning about saving over a name already defined
796 ourOptions = Arrays.asList(options);
800 // use a JOptionPane as usual
801 int response = JOptionPane.showOptionDialog(parentComponent, message,
802 title, optionType, messageType, icon, options, initialValue);
805 * In Java, the response is returned to this thread and handled here;
806 * (for Javascript, see propertyChange)
808 if (!Platform.isJS())
815 handleResponse(response);
821 * This is java similar to the swingjs handling, with the callbacks
822 * attached to the button press of the dialog. This means we can use
823 * a non-modal JDialog for the confirmation without blocking the GUI.
825 JOptionPane joptionpane = new JOptionPane();
826 // Make button options
827 int[] buttonActions = { JvOptionPane.YES_OPTION,
828 JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION };
830 // we need the strings to make the buttons with actionEventListener
833 ArrayList<String> options_default = new ArrayList<>();
835 .add(UIManager.getString("OptionPane.yesButtonText"));
836 if (optionType == JvOptionPane.YES_NO_OPTION
837 || optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
840 .add(UIManager.getString("OptionPane.noButtonText"));
842 if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION)
845 .add(UIManager.getString("OptionPane.cancelButtonText"));
847 options = options_default.toArray();
850 ArrayList<JButton> options_btns = new ArrayList<>();
851 Object initialValue_btn = null;
852 if (!Platform.isJS()) // JalviewJS already uses callback, don't need to add them here
854 for (int i = 0; i < options.length && i < 3; i++)
856 Object o = options[i];
857 int buttonAction = buttonActions[i];
858 Runnable action = callbacks.get(buttonAction);
859 JButton jb = new JButton();
860 jb.setText((String) o);
861 jb.addActionListener(new ActionListener()
864 public void actionPerformed(ActionEvent e)
866 joptionpane.setValue(buttonAction);
868 Executors.defaultThreadFactory().newThread(action).start();
869 // joptionpane.transferFocusBackward();
870 joptionpane.transferFocusBackward();
871 joptionpane.setVisible(false);
872 // put focus and raise parent window if possible, unless cancel
874 boolean raiseParent = (parentComponent != null);
875 if (buttonAction == JvOptionPane.CANCEL_OPTION)
877 if (optionType == JvOptionPane.YES_NO_OPTION
878 && buttonAction == JvOptionPane.NO_OPTION)
882 parentComponent.requestFocus();
883 if (parentComponent instanceof JInternalFrame)
885 JInternalFrame jif = (JInternalFrame) parentComponent;
890 else if (parentComponent instanceof Window)
892 Window w = (Window) parentComponent;
897 joptionpane.setVisible(false);
900 options_btns.add(jb);
901 if (o.equals(initialValue))
902 initialValue_btn = jb;
905 joptionpane.setMessage(message);
906 joptionpane.setMessageType(messageType);
907 joptionpane.setOptionType(optionType);
908 joptionpane.setIcon(icon);
909 joptionpane.setOptions(
910 Platform.isJS() ? options : options_btns.toArray());
911 joptionpane.setInitialValue(
912 Platform.isJS() ? initialValue : initialValue_btn);
914 JDialog dialog = joptionpane.createDialog(parentComponent, title);
915 dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL
916 : ModalityType.MODELESS);
917 dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
918 dialog.setVisible(true);
922 public void showInternalDialog(JPanel mainPanel, String title,
923 int yesNoCancelOption, int questionMessage, Icon icon,
924 Object[] options, String initresponse)
926 if (!isInteractiveMode())
928 handleResponse(getMockResponse());
931 ourOptions = Arrays.asList(options);
933 if (parentComponent != this)
935 response = JOptionPane.showInternalOptionDialog(parentComponent,
936 mainPanel, title, yesNoCancelOption, questionMessage, icon,
937 options, initresponse);
941 response = JOptionPane.showOptionDialog(parentComponent, mainPanel,
942 title, yesNoCancelOption, questionMessage, icon, options,
945 if (!Platform.isJS())
952 handleResponse(response);
957 public JvOptionPane setResponseHandler(Object response, Runnable action)
959 callbacks.put(response, action);
964 * JalviewJS signals option selection by a property change event for the
965 * option e.g. "OK". This methods responds to that by running the response
966 * action that corresponds to that option.
971 public void propertyChange(PropertyChangeEvent evt)
973 Object newValue = evt.getNewValue();
974 int ourOption = ourOptions.indexOf(newValue);
977 handleResponse(ourOption);
982 handleResponse(newValue);
987 public void handleResponse(Object response)
990 * this test is for NaN in Chrome
992 if (response != null && !response.equals(response))
996 Runnable action = callbacks.get(response);
1000 parentComponent.requestFocus();