X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FJvOptionPane.java;h=028e50b0d1e646efdc24cdc65aebeb12d5939180;hb=53fe06e7ae7a8c00ec902413391cea15362bdbf2;hp=7b2e430d9d1f786b3482cc983a7d65d8b3878177;hpb=f4766a7bbcfae845fc95923b01fa14ff83d589ff;p=jalview.git diff --git a/src/jalview/gui/JvOptionPane.java b/src/jalview/gui/JvOptionPane.java index 7b2e430..028e50b 100644 --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@ -22,239 +22,603 @@ package jalview.gui; import java.awt.Component; +import java.awt.Dialog.ModalityType; import java.awt.HeadlessException; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JInternalFrame; import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.UIManager; + +import jalview.util.Platform; +import jalview.util.dialogrunner.DialogRunnerI; public class JvOptionPane extends JOptionPane + implements DialogRunnerI, PropertyChangeListener { - /** - * - */ private static final long serialVersionUID = -3019167117756785229L; private static Object mockResponse = JvOptionPane.CANCEL_OPTION; private static boolean interactiveMode = true; + private Component parentComponent; + + private Map callbacks = new HashMap<>(); + + /* + * JalviewJS reports user choice in the dialog as the selected + * option (text); this list allows conversion to index (int) + */ + List ourOptions; + + public JvOptionPane(final Component parent) + { + this.parentComponent = Platform.isJS() ? this : parent; + } + public static int showConfirmDialog(Component parentComponent, Object message) throws HeadlessException { + // only called by test return isInteractiveMode() ? JOptionPane.showConfirmDialog(parentComponent, message) : (int) getMockResponse(); } + /** + * Message, title, optionType + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @return + * @throws HeadlessException + */ public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) throws HeadlessException { - return isInteractiveMode() - ? JOptionPane.showConfirmDialog(parentComponent, message, title, - optionType) - : (int) getMockResponse(); + if (!isInteractiveMode()) + { + return (int) getMockResponse(); + } + switch (optionType) + { + case JvOptionPane.YES_NO_CANCEL_OPTION: + // FeatureRenderer amendFeatures ?? TODO ?? + // Chimera close + // PromptUserConfig + // $FALL-THROUGH$ + default: + case JvOptionPane.YES_NO_OPTION: + // PromptUserConfig usage stats + // for now treated as "OK CANCEL" + // $FALL-THROUGH$ + case JvOptionPane.OK_CANCEL_OPTION: + // will fall back to simple HTML + return JOptionPane.showConfirmDialog(parentComponent, message, title, + optionType); + } } + /** + * Adds a message type. Fallback is to just add it in the beginning. + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @param messageType + * @return + * @throws HeadlessException + */ public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) throws HeadlessException { + // JalviewServicesChanged + // PromptUserConfig raiseDialog return isInteractiveMode() ? JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType) : (int) getMockResponse(); } + /** + * Adds an icon + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @param messageType + * @param icon + * @return + * @throws HeadlessException + */ public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType, Icon icon) throws HeadlessException { + // JvOptionPaneTest only return isInteractiveMode() ? JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType, icon) : (int) getMockResponse(); } + /** + * Internal version "OK" + * + * @param parentComponent + * @param message + * @return + */ public static int showInternalConfirmDialog(Component parentComponent, Object message) { + // JvOptionPaneTest only; return isInteractiveMode() ? JOptionPane.showInternalConfirmDialog(parentComponent, message) : (int) getMockResponse(); } + /** + * Internal version -- changed to standard version for now + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @return + */ public static int showInternalConfirmDialog(Component parentComponent, - Object message, String title, int optionType) + String message, String title, int optionType) { - return isInteractiveMode() - ? JOptionPane.showConfirmDialog(parentComponent, message, title, - optionType) - : (int) getMockResponse(); + if (!isInteractiveMode()) + { + return (int) getMockResponse(); + } + switch (optionType) + { + case JvOptionPane.YES_NO_CANCEL_OPTION: + // ColourMenuHelper.addMenuItmers.offerRemoval TODO + case JvOptionPane.YES_NO_OPTION: + // UserDefinedColoursSave -- relevant? TODO + // $FALL-THROUGH$ + default: + case JvOptionPane.OK_CANCEL_OPTION: + + // EditNameDialog --- uses panel for messsage TODO + + // Desktop.inputURLMenuItem + // WsPreferenses + return JOptionPane.showConfirmDialog(parentComponent, message, title, + optionType); + } } + /** + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @param messageType + * @return + */ public static int showInternalConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) { - return isInteractiveMode() - ? JOptionPane.showConfirmDialog(parentComponent, message, title, - optionType, messageType) - : (int) getMockResponse(); + if (!isInteractiveMode()) + { + return (int) getMockResponse(); + } + switch (optionType) + { + case JvOptionPane.YES_NO_CANCEL_OPTION: + case JvOptionPane.YES_NO_OPTION: + // UserQuestionanaireCheck + // VamsasApplication + // $FALL-THROUGH$ + default: + case JvOptionPane.OK_CANCEL_OPTION: + // will fall back to simple HTML + return JOptionPane.showConfirmDialog(parentComponent, message, title, + optionType, messageType); + } } + /** + * adds icon; no longer internal + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @param messageType + * @param icon + * @return + */ public static int showInternalConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType, Icon icon) { - return isInteractiveMode() - ? JOptionPane.showInternalConfirmDialog(parentComponent, - message, title, optionType, messageType, icon) - : (int) getMockResponse(); + if (!isInteractiveMode()) + { + return (int) getMockResponse(); + } + switch (optionType) + { + case JvOptionPane.YES_NO_CANCEL_OPTION: + case JvOptionPane.YES_NO_OPTION: + //$FALL-THROUGH$ + default: + case JvOptionPane.OK_CANCEL_OPTION: + // Preferences editLink/newLink + return JOptionPane.showConfirmDialog(parentComponent, message, title, + optionType, messageType, icon); + } + } + /** + * custom options full-featured + * + * @param parentComponent + * @param message + * @param title + * @param optionType + * @param messageType + * @param icon + * @param options + * @param initialValue + * @return + * @throws HeadlessException + */ public static int showOptionDialog(Component parentComponent, - Object message, String title, int optionType, int messageType, + String message, String title, int optionType, int messageType, Icon icon, Object[] options, Object initialValue) throws HeadlessException { - return isInteractiveMode() - ? JOptionPane.showOptionDialog(parentComponent, message, title, - optionType, messageType, icon, options, initialValue) - : (int) getMockResponse(); + if (!isInteractiveMode()) + { + return (int) getMockResponse(); + } + // two uses: + // + // TODO + // + // 1) AlignViewport for openLinkedAlignment + // + // Show a dialog with the option to open and link (cDNA <-> protein) as a + // new + // alignment, either as a standalone alignment or in a split frame. Returns + // true if the new alignment was opened, false if not, because the user + // declined the offer. + // + // 2) UserDefinedColors warning about saving over a name already defined + // + return JOptionPane.showOptionDialog(parentComponent, message, title, + optionType, messageType, icon, options, initialValue); } + /** + * Just an OK message + * + * @param message + * @throws HeadlessException + */ public static void showMessageDialog(Component parentComponent, - Object message) throws HeadlessException + String message) throws HeadlessException { - if (isInteractiveMode()) - { - JOptionPane.showMessageDialog(parentComponent, message); - } - else + if (!isInteractiveMode()) { outputMessage(message); + return; } + + // test class only + + JOptionPane.showMessageDialog(parentComponent, message); } + /** + * OK with message, title, and type + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @throws HeadlessException + */ public static void showMessageDialog(Component parentComponent, - Object message, String title, int messageType) + String message, String title, int messageType) throws HeadlessException { - if (isInteractiveMode()) - { - JOptionPane.showMessageDialog(parentComponent, message, title, - messageType); - } - else + // 30 implementations -- all just fine. + + if (!isInteractiveMode()) { outputMessage(message); + return; } + + JOptionPane.showMessageDialog(parentComponent, + getPrefix(messageType) + message, title, messageType); } + /** + * adds title and icon + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @param icon + * @throws HeadlessException + */ public static void showMessageDialog(Component parentComponent, - Object message, String title, int messageType, Icon icon) + String message, String title, int messageType, Icon icon) throws HeadlessException { - if (isInteractiveMode()) - { - JOptionPane.showMessageDialog(parentComponent, message, title, - messageType, icon); - } - else + + // test only + + if (!isInteractiveMode()) { outputMessage(message); + return; } + + JOptionPane.showMessageDialog(parentComponent, message, title, + messageType, icon); } + /** + * was internal + * + */ public static void showInternalMessageDialog(Component parentComponent, Object message) { - if (isInteractiveMode()) - { - JOptionPane.showMessageDialog(parentComponent, message); - } - else + + // WsPreferences only + + if (!isInteractiveMode()) { outputMessage(message); + return; } + + JOptionPane.showMessageDialog(parentComponent, message); } + /** + * Adds title and messageType + * + * @param parentComponent + * @param message + * @param title + * @param messageType + */ public static void showInternalMessageDialog(Component parentComponent, - Object message, String title, int messageType) + String message, String title, int messageType) { - if (isInteractiveMode()) - { - JOptionPane.showMessageDialog(parentComponent, message, title, - messageType); - } - else + + // 41 references + + if (!isInteractiveMode()) { outputMessage(message); + return; } + + JOptionPane.showMessageDialog(parentComponent, + getPrefix(messageType) + message, title, messageType); } + /** + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @param icon + */ public static void showInternalMessageDialog(Component parentComponent, Object message, String title, int messageType, Icon icon) { - if (isInteractiveMode()) - { - JOptionPane.showMessageDialog(parentComponent, message, title, - messageType, icon); - } - else + + // test only + + if (!isInteractiveMode()) { outputMessage(message); + return; } + + JOptionPane.showMessageDialog(parentComponent, message, title, + messageType, icon); } + /** + * + * @param message + * @return + * @throws HeadlessException + */ public static String showInputDialog(Object message) throws HeadlessException { - return isInteractiveMode() ? JOptionPane.showInputDialog(message) - : getMockResponse().toString(); + // test only + + if (!isInteractiveMode()) + { + return getMockResponse().toString(); + } + + return JOptionPane.showInputDialog(message); } + /** + * adds inital selection value + * + * @param message + * @param initialSelectionValue + * @return + */ + public static String showInputDialog(String message, + String initialSelectionValue) + { + if (!isInteractiveMode()) + { + return getMockResponse().toString(); + } + + // AnnotationPanel character option + + return JOptionPane.showInputDialog(message, initialSelectionValue); + } + + /** + * adds inital selection value + * + * @param message + * @param initialSelectionValue + * @return + */ public static String showInputDialog(Object message, Object initialSelectionValue) { + if (!isInteractiveMode()) + { + return getMockResponse().toString(); + } + + // AnnotationPanel character option + + return JOptionPane.showInputDialog(message, initialSelectionValue); + } + + /** + * centered on parent + * + * @param parentComponent + * @param message + * @return + * @throws HeadlessException + */ + public static String showInputDialog(Component parentComponent, + String message) throws HeadlessException + { + // test only + return isInteractiveMode() - ? JOptionPane.showInputDialog(message, initialSelectionValue) + ? JOptionPane.showInputDialog(parentComponent, message) : getMockResponse().toString(); } + /** + * input with initial selection + * + * @param parentComponent + * @param message + * @param initialSelectionValue + * @return + */ public static String showInputDialog(Component parentComponent, - Object message) throws HeadlessException + String message, String initialSelectionValue) { + + // AnnotationPanel + return isInteractiveMode() - ? JOptionPane.showInputDialog(parentComponent, message) + ? JOptionPane.showInputDialog(parentComponent, message, + initialSelectionValue) : getMockResponse().toString(); } + /** + * input with initial selection + * + * @param parentComponent + * @param message + * @param initialSelectionValue + * @return + */ public static String showInputDialog(Component parentComponent, Object message, Object initialSelectionValue) { + + // AnnotationPanel + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, initialSelectionValue) : getMockResponse().toString(); } + /** + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @return + * @throws HeadlessException + */ public static String showInputDialog(Component parentComponent, - Object message, String title, int messageType) + String message, String title, int messageType) throws HeadlessException { + + // test only + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, title, messageType) : getMockResponse().toString(); } + /** + * Customized input option + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @param icon + * @param selectionValues + * @param initialSelectionValue + * @return + * @throws HeadlessException + */ public static Object showInputDialog(Component parentComponent, Object message, String title, int messageType, Icon icon, Object[] selectionValues, Object initialSelectionValue) throws HeadlessException { + + // test only + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, title, messageType, icon, selectionValues, @@ -262,27 +626,62 @@ public class JvOptionPane extends JOptionPane : getMockResponse().toString(); } + /** + * internal version + * + * @param parentComponent + * @param message + * @return + */ public static String showInternalInputDialog(Component parentComponent, - Object message) + String message) { + // test only + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, message) : getMockResponse().toString(); } + /** + * internal with title and messageType + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @return + */ public static String showInternalInputDialog(Component parentComponent, - Object message, String title, int messageType) + String message, String title, int messageType) { + + // AlignFrame tabbedPane_mousePressed + return isInteractiveMode() - ? JOptionPane.showInternalInputDialog(parentComponent, message, - title, messageType) + ? JOptionPane.showInternalInputDialog(parentComponent, + getPrefix(messageType) + message, title, messageType) : getMockResponse().toString(); } + /** + * customized internal + * + * @param parentComponent + * @param message + * @param title + * @param messageType + * @param icon + * @param selectionValues + * @param initialSelectionValue + * @return + */ public static Object showInternalInputDialog(Component parentComponent, - Object message, String title, int messageType, Icon icon, + String message, String title, int messageType, Icon icon, Object[] selectionValues, Object initialSelectionValue) { + // test only + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, message, title, messageType, icon, selectionValues, @@ -290,6 +689,8 @@ public class JvOptionPane extends JOptionPane : getMockResponse().toString(); } + ///////////// end of options /////////////// + private static void outputMessage(Object message) { System.out.println(">>> JOption Message : " + message.toString()); @@ -316,9 +717,287 @@ public class JvOptionPane extends JOptionPane return interactiveMode; } - public static void setInteractiveMode(boolean interactiveMode) + public static void setInteractiveMode(boolean interactive) + { + JvOptionPane.interactiveMode = interactive; + } + + private static String getPrefix(int messageType) + { + String prefix = ""; + + // JavaScript only + if (Platform.isJS()) + { + switch (messageType) + { + case JvOptionPane.WARNING_MESSAGE: + prefix = "WARNING! "; + break; + case JvOptionPane.ERROR_MESSAGE: + prefix = "ERROR! "; + break; + default: + prefix = "Note: "; + } + } + return prefix; + } + + /** + * create a new option dialog that can be used to register responses - along + * lines of showOptionDialog + * + * @param desktop + * @param question + * @param string + * @param defaultOption + * @param plainMessage + * @param object + * @param options + * @param string2 + * @return + */ + public static JvOptionPane newOptionDialog(Component parentComponent) + { + return new JvOptionPane(parentComponent); + } + + public void showDialog(String message, String title, int optionType, + int messageType, Icon icon, Object[] options, Object initialValue) + { + showDialog(message, title, optionType, messageType, icon, options, + initialValue, true); + } + + public void showDialog(String message, String title, int optionType, + int messageType, Icon icon, Object[] options, Object initialValue, + boolean modal) + { + if (!isInteractiveMode()) + { + handleResponse(getMockResponse()); + } + // two uses: + // + // TODO + // + // 1) AlignViewport for openLinkedAlignment + // + // Show a dialog with the option to open and link (cDNA <-> protein) as a + // new + // alignment, either as a standalone alignment or in a split frame. Returns + // true if the new alignment was opened, false if not, because the user + // declined the offer. + // + // 2) UserDefinedColors warning about saving over a name already defined + // + + ourOptions = Arrays.asList(options); + + if (modal) + { + // use a JOptionPane as usual + int response = JOptionPane.showOptionDialog(parentComponent, message, + title, optionType, messageType, icon, options, initialValue); + + /* + * In Java, the response is returned to this thread and handled here; + * (for Javascript, see propertyChange) + */ + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ + { + handleResponse(response); + } + } + else + { + /* + * This is java similar to the swingjs handling, with the callbacks + * attached to the button press of the dialog. This means we can use + * a non-modal JDialog for the confirmation without blocking the GUI. + */ + JOptionPane joptionpane = new JOptionPane(); + // Make button options + int[] buttonActions = { JvOptionPane.YES_OPTION, + JvOptionPane.NO_OPTION, JvOptionPane.CANCEL_OPTION }; + + // we need the strings to make the buttons with actionEventListener + if (options == null) + { + ArrayList options_default = new ArrayList<>(); + options_default + .add(UIManager.getString("OptionPane.yesButtonText")); + if (optionType == JvOptionPane.YES_NO_OPTION + || optionType == JvOptionPane.YES_NO_CANCEL_OPTION) + { + options_default + .add(UIManager.getString("OptionPane.noButtonText")); + } + if (optionType == JvOptionPane.YES_NO_CANCEL_OPTION) + { + options_default + .add(UIManager.getString("OptionPane.cancelButtonText")); + } + options = options_default.toArray(); + } + + ArrayList options_btns = new ArrayList<>(); + Object initialValue_btn = null; + if (!Platform.isJS()) // JalviewJS already uses callback, don't need to add them here + { + for (int i = 0; i < options.length && i < 3; i++) + { + Object o = options[i]; + int buttonAction = buttonActions[i]; + Runnable action = callbacks.get(buttonAction); + JButton jb = new JButton(); + jb.setText((String) o); + jb.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + joptionpane.setValue(buttonAction); + if (action != null) + Executors.defaultThreadFactory().newThread(action).start(); + // joptionpane.transferFocusBackward(); + joptionpane.transferFocusBackward(); + joptionpane.setVisible(false); + // put focus and raise parent window if possible, unless cancel + // button pressed + boolean raiseParent = (parentComponent != null); + if (buttonAction == JvOptionPane.CANCEL_OPTION) + raiseParent = false; + if (optionType == JvOptionPane.YES_NO_OPTION + && buttonAction == JvOptionPane.NO_OPTION) + raiseParent = false; + if (raiseParent) + { + parentComponent.requestFocus(); + if (parentComponent instanceof JInternalFrame) + { + JInternalFrame jif = (JInternalFrame) parentComponent; + jif.show(); + jif.moveToFront(); + jif.grabFocus(); + } + else if (parentComponent instanceof Window) + { + Window w = (Window) parentComponent; + w.toFront(); + w.requestFocus(); + } + } + joptionpane.setVisible(false); + } + }); + options_btns.add(jb); + if (o.equals(initialValue)) + initialValue_btn = jb; + } + } + joptionpane.setMessage(message); + joptionpane.setMessageType(messageType); + joptionpane.setOptionType(optionType); + joptionpane.setIcon(icon); + joptionpane.setOptions( + Platform.isJS() ? options : options_btns.toArray()); + joptionpane.setInitialValue( + Platform.isJS() ? initialValue : initialValue_btn); + + JDialog dialog = joptionpane.createDialog(parentComponent, title); + dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL + : ModalityType.MODELESS); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + } + } + + public void showInternalDialog(JPanel mainPanel, String title, + int yesNoCancelOption, int questionMessage, Icon icon, + Object[] options, String initresponse) + { + if (!isInteractiveMode()) + { + handleResponse(getMockResponse()); + } + + ourOptions = Arrays.asList(options); + int response; + if (parentComponent != this) + { + response = JOptionPane.showInternalOptionDialog(parentComponent, + mainPanel, title, yesNoCancelOption, questionMessage, icon, + options, initresponse); + } + else + { + response = JOptionPane.showOptionDialog(parentComponent, mainPanel, + title, yesNoCancelOption, questionMessage, icon, options, + initresponse); + } + if (!Platform.isJS()) + /** + * Java only + * + * @j2sIgnore + */ + { + handleResponse(response); + } + } + + @Override + public JvOptionPane setResponseHandler(Object response, Runnable action) + { + callbacks.put(response, action); + return this; + } + + /** + * JalviewJS signals option selection by a property change event for the + * option e.g. "OK". This methods responds to that by running the response + * action that corresponds to that option. + * + * @param evt + */ + @Override + public void propertyChange(PropertyChangeEvent evt) { - JvOptionPane.interactiveMode = interactiveMode; + Object newValue = evt.getNewValue(); + int ourOption = ourOptions.indexOf(newValue); + if (ourOption >= 0) + { + handleResponse(ourOption); + } + else + { + // try our luck.. + handleResponse(newValue); + } } + @Override + public void handleResponse(Object response) + { + /* + * this test is for NaN in Chrome + */ + if (response != null && !response.equals(response)) + { + return; + } + Runnable action = callbacks.get(response); + if (action != null) + { + action.run(); + parentComponent.requestFocus(); + } + } }