X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FJvOptionPane.java;h=c5427cb1c224075b4dd8d90ed067e6ec074321fb;hb=b364e1e6d199002069dab615d1007799b5bb71e1;hp=5bbe75f9bc73a6bf37ac748f5c656abce0b44e5c;hpb=26b115b0a77d521da92a06572d9b7819c2d0d49a;p=jalview.git diff --git a/src/jalview/gui/JvOptionPane.java b/src/jalview/gui/JvOptionPane.java index 5bbe75f..c5427cb 100644 --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@ -21,41 +21,53 @@ package jalview.gui; -import jalview.util.Platform; -import jalview.util.dialogrunner.DialogRunnerI; - 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.Callable; +import java.util.concurrent.Executors; import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.UIManager; -public class JvOptionPane extends JOptionPane implements DialogRunnerI, - PropertyChangeListener +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 Object mockResponse = JOptionPane.CANCEL_OPTION; private static boolean interactiveMode = true; private Component parentComponent; - private Map callbacks = new HashMap<>(); + 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; @@ -90,17 +102,17 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, } switch (optionType) { - case JvOptionPane.YES_NO_CANCEL_OPTION: - // FeatureRenderer amendFeatures ?? TODO ?? + case JOptionPane.YES_NO_CANCEL_OPTION: + // FeatureRenderer amendFeatures ?? TODO ?? // Chimera close // PromptUserConfig // $FALL-THROUGH$ default: - case JvOptionPane.YES_NO_OPTION: + case JOptionPane.YES_NO_OPTION: // PromptUserConfig usage stats // for now treated as "OK CANCEL" // $FALL-THROUGH$ - case JvOptionPane.OK_CANCEL_OPTION: + case JOptionPane.OK_CANCEL_OPTION: // will fall back to simple HTML return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType); @@ -164,8 +176,10 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, Object message) { // JvOptionPaneTest only; - return isInteractiveMode() ? JOptionPane.showInternalConfirmDialog( - parentComponent, message) : (int) getMockResponse(); + return isInteractiveMode() + ? JOptionPane.showInternalConfirmDialog(parentComponent, + message) + : (int) getMockResponse(); } /** @@ -186,13 +200,13 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, } switch (optionType) { - case JvOptionPane.YES_NO_CANCEL_OPTION: + case JOptionPane.YES_NO_CANCEL_OPTION: // ColourMenuHelper.addMenuItmers.offerRemoval TODO - case JvOptionPane.YES_NO_OPTION: + case JOptionPane.YES_NO_OPTION: // UserDefinedColoursSave -- relevant? TODO // $FALL-THROUGH$ default: - case JvOptionPane.OK_CANCEL_OPTION: + case JOptionPane.OK_CANCEL_OPTION: // EditNameDialog --- uses panel for messsage TODO @@ -221,13 +235,13 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, } switch (optionType) { - case JvOptionPane.YES_NO_CANCEL_OPTION: - case JvOptionPane.YES_NO_OPTION: + case JOptionPane.YES_NO_CANCEL_OPTION: + case JOptionPane.YES_NO_OPTION: // UserQuestionanaireCheck // VamsasApplication // $FALL-THROUGH$ default: - case JvOptionPane.OK_CANCEL_OPTION: + case JOptionPane.OK_CANCEL_OPTION: // will fall back to simple HTML return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType); @@ -255,11 +269,11 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, } switch (optionType) { - case JvOptionPane.YES_NO_CANCEL_OPTION: - case JvOptionPane.YES_NO_OPTION: + case JOptionPane.YES_NO_CANCEL_OPTION: + case JOptionPane.YES_NO_OPTION: //$FALL-THROUGH$ default: - case JvOptionPane.OK_CANCEL_OPTION: + case JOptionPane.OK_CANCEL_OPTION: // Preferences editLink/newLink return JOptionPane.showConfirmDialog(parentComponent, message, title, optionType, messageType, icon); @@ -399,7 +413,6 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, JOptionPane.showMessageDialog(parentComponent, message); } - /** * Adds title and messageType * @@ -506,6 +519,7 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, return JOptionPane.showInputDialog(message, initialSelectionValue); } + /** * centered on parent * @@ -535,15 +549,14 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, public static String showInputDialog(Component parentComponent, String message, String initialSelectionValue) { - + // AnnotationPanel - + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, initialSelectionValue) : getMockResponse().toString(); } - /** * input with initial selection @@ -556,9 +569,9 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, public static String showInputDialog(Component parentComponent, Object message, Object initialSelectionValue) { - + // AnnotationPanel - + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, initialSelectionValue) @@ -581,13 +594,14 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, // test only - return isInteractiveMode() ? JOptionPane - .showInputDialog(parentComponent, message, title, messageType) + return isInteractiveMode() + ? JOptionPane.showInputDialog(parentComponent, message, title, + messageType) : getMockResponse().toString(); } /** - * Customized input option + * Customized input option * * @param parentComponent * @param message @@ -604,9 +618,9 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, Object[] selectionValues, Object initialSelectionValue) throws HeadlessException { - + // test only - + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, title, messageType, icon, selectionValues, @@ -614,8 +628,6 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, : getMockResponse().toString(); } - - /** * internal version * @@ -627,13 +639,12 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, String message) { // test only - + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, message) : getMockResponse().toString(); } - /** * internal with title and messageType * @@ -646,9 +657,9 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, public static String showInternalInputDialog(Component parentComponent, String message, String title, int messageType) { - + // AlignFrame tabbedPane_mousePressed - + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, getPrefix(messageType) + message, title, messageType) @@ -672,7 +683,7 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, Object[] selectionValues, Object initialSelectionValue) { // test only - + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, message, title, messageType, icon, selectionValues, @@ -680,10 +691,8 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, : getMockResponse().toString(); } - ///////////// end of options /////////////// - - + private static void outputMessage(Object message) { System.out.println(">>> JOption Message : " + message.toString()); @@ -701,7 +710,7 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, public static void resetMock() { - setMockResponse(JvOptionPane.CANCEL_OPTION); + setMockResponse(JOptionPane.CANCEL_OPTION); setInteractiveMode(true); } @@ -717,17 +726,17 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, private static String getPrefix(int messageType) { - String prefix = ""; - + String prefix = ""; + // JavaScript only if (Platform.isJS()) { switch (messageType) { - case JvOptionPane.WARNING_MESSAGE: + case JOptionPane.WARNING_MESSAGE: prefix = "WARNING! "; break; - case JvOptionPane.ERROR_MESSAGE: + case JOptionPane.ERROR_MESSAGE: prefix = "ERROR! "; break; default: @@ -751,16 +760,27 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, * @param string2 * @return */ + public static JvOptionPane newOptionDialog() + { + return new JvOptionPane(null); + } + 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) + 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()); @@ -779,19 +799,42 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, // // 2) UserDefinedColors warning about saving over a name already defined // - + ourOptions = Arrays.asList(options); - - 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()) + + if (modal) { - handleResponse(response); + // 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. + */ + + JDialog dialog = this.createDialog(parentComponent, message, title, + optionType, messageType, icon, options, initialValue, modal); + jalview.bin.Console.debug("About to setVisible(true)"); + dialog.setVisible(true); + jalview.bin.Console.debug("Just setVisible(true)"); } } @@ -803,38 +846,113 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, { handleResponse(getMockResponse()); } - + ourOptions = Arrays.asList(options); int response; - if (parentComponent != this) + if (parentComponent != this) { - response = JOptionPane.showInternalOptionDialog(parentComponent, mainPanel, - title, yesNoCancelOption, questionMessage, icon, options, - initresponse); + response = JOptionPane.showInternalOptionDialog(parentComponent, + mainPanel, title, yesNoCancelOption, questionMessage, icon, + options, initresponse); } else { - response = JOptionPane.showOptionDialog(parentComponent, mainPanel, title, - yesNoCancelOption, questionMessage, icon, options, + 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); + callbacks.put(response, new Callable() + { + @Override + public Void call() + { + action.run(); + return null; + } + }); return this; } + */ + @Override + public JvOptionPane setResponseHandler(Object response, Callable action) + { + callbacks.put(response, action); + return this; + } + + /** + * showDialogOnTop will create a dialog that (attempts to) come to top of OS + * desktop windows + */ + public static int showDialogOnTop(String label, String actionString, + int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE) + { + // Ensure Jalview window is brought to front (primarily for Quit + // confirmation window to be visible) + + // This method of raising the Jalview window is broken in java + // jalviewDesktop.setVisible(true); + // jalviewDesktop.toFront(); + + // A better hack which works is to create a new JFrame parent with + // setAlwaysOnTop(true) + JFrame dialogParent = new JFrame(); + dialogParent.setAlwaysOnTop(true); + + int answer = JOptionPane.showConfirmDialog(dialogParent, label, + actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE); + + dialogParent.setAlwaysOnTop(false); + jalview.bin.Console.debug("*********** BEFORE dialogParent.dispose()"); + dialogParent.dispose(); + jalview.bin.Console.debug("*********** BEFORE dialogParent.dispose()"); + + return answer; + } + + public void showDialogOnTopAsync(String label, String actionString, + int JOPTIONPANE_OPTION, int JOPTIONPANE_MESSAGETYPE, Icon icon, + Object[] options, Object initialValue, boolean modal) + { + // Ensure Jalview window is brought to front (primarily for Quit + // confirmation window to be visible) + + // This method of raising the Jalview window is broken in java + // jalviewDesktop.setVisible(true); + // jalviewDesktop.toFront(); + + // A better hack which works is to create a new JFrame parent with + // setAlwaysOnTop(true) + JFrame dialogParent = new JFrame(); + dialogParent.setAlwaysOnTop(true); + parentComponent = dialogParent; + + showDialog(label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal); + + dialogParent.setAlwaysOnTop(false); + dialogParent.dispose(); + } /** - * 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. + * 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 */ @@ -842,7 +960,7 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, public void propertyChange(PropertyChangeEvent evt) { Object newValue = evt.getNewValue(); - int ourOption = ourOptions.indexOf(newValue); + int ourOption = ourOptions.indexOf(newValue); if (ourOption >= 0) { handleResponse(ourOption); @@ -858,16 +976,145 @@ public class JvOptionPane extends JOptionPane implements DialogRunnerI, public void handleResponse(Object response) { /* - * this test is for NaN in Chrome - */ + * this test is for NaN in Chrome + */ if (response != null && !response.equals(response)) { return; } - Runnable action = callbacks.get(response); + Callable action = callbacks.get(response); if (action != null) { - action.run(); + try + { + action.call(); + } catch (Exception e) + { + e.printStackTrace(); + } + if (parentComponent != null) + parentComponent.requestFocus(); } } + + /** + * Create a non-modal confirm dialog + */ + public JDialog createDialog(Component parentComponent, String message, + String title, int optionType, int messageType, Icon icon, + Object[] options, Object initialValue, boolean modal) + { + JOptionPane joptionpane = new JOptionPane(); + // Make button options + int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION, + JOptionPane.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 == JOptionPane.YES_NO_OPTION + || optionType == JOptionPane.YES_NO_CANCEL_OPTION) + { + options_default.add(UIManager.getString("OptionPane.noButtonText")); + } + if (optionType == JOptionPane.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 + { + if (((optionType == JOptionPane.YES_NO_OPTION + || optionType == JOptionPane.OK_CANCEL_OPTION) + && options.length < 2) + || (optionType == JOptionPane.YES_NO_CANCEL_OPTION + && options.length < 3)) + { + jalview.bin.Console + .debug("JvOptionPane: not enough options for dialog type"); + } + for (int i = 0; i < options.length && i < 3; i++) + { + Object o = options[i]; + int buttonAction = buttonActions[i]; + Callable 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.newSingleThreadExecutor().submit(action); + // 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 == JOptionPane.CANCEL_OPTION) + raiseParent = false; + if (optionType == JOptionPane.YES_NO_OPTION + && buttonAction == JOptionPane.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); + return dialog; + } + + /** + * Utility to programmatically click a button on a JOptionPane (as a JFrame) + * + * returns true if button was found + */ + public static boolean clickButton(JFrame frame, int buttonType) + { + + return false; + } }