X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FJvOptionPane.java;h=3f5de0abd5711a7a5bb13d77f9cfd5be95e9ba61;hb=8dca84768d848bc368985a8f0938a6c7ac059952;hp=89899e2532bf50f5cbde66ae708b6aa216ff0637;hpb=13391b836bdc59c53cd8e8aba8d449629307d9b7;p=jalview.git diff --git a/src/jalview/gui/JvOptionPane.java b/src/jalview/gui/JvOptionPane.java index 89899e2..3f5de0a 100644 --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@ -18,41 +18,86 @@ * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ - package jalview.gui; -import jalview.util.dialogrunner.DialogRunner; -import jalview.util.dialogrunner.DialogRunnerI; -import jalview.util.dialogrunner.RunResponse; - +import java.awt.AWTEvent; +import java.awt.ActiveEvent; import java.awt.Component; +import java.awt.Container; +import java.awt.Dialog.ModalityType; +import java.awt.EventQueue; import java.awt.HeadlessException; +import java.awt.MenuComponent; +import java.awt.Toolkit; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +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.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; + +import jalview.bin.Console; +import jalview.util.ChannelProperties; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.util.dialogrunner.DialogRunnerI; public class JvOptionPane extends JOptionPane implements DialogRunnerI, PropertyChangeListener { - // BH 2018 no changes needed here. - private static final long serialVersionUID = -3019167117756785229L; private static Object mockResponse = JvOptionPane.CANCEL_OPTION; private static boolean interactiveMode = true; + public static final Runnable NULLCALLABLE = () -> { + }; + private Component parentComponent; - public JvOptionPane(final Component parentComponent) + private ExecutorService executor = Executors.newCachedThreadPool(); + + private JDialog dialog = null; + + 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 = parentComponent; + this.parentComponent = Platform.isJS() ? this : parent; + this.setIcon(null); } public static int showConfirmDialog(Component parentComponent, @@ -85,7 +130,7 @@ public class JvOptionPane extends JOptionPane switch (optionType) { case JvOptionPane.YES_NO_CANCEL_OPTION: - // FeatureRenderer amendFeatures ?? TODO ?? + // FeatureRenderer amendFeatures ?? TODO ?? // Chimera close // PromptUserConfig // $FALL-THROUGH$ @@ -158,8 +203,10 @@ public class JvOptionPane extends JOptionPane Object message) { // JvOptionPaneTest only; - return isInteractiveMode() ? JOptionPane.showInternalConfirmDialog( - parentComponent, message) : (int) getMockResponse(); + return isInteractiveMode() + ? JOptionPane.showInternalConfirmDialog(parentComponent, + message) + : (int) getMockResponse(); } /** @@ -393,7 +440,6 @@ public class JvOptionPane extends JOptionPane JOptionPane.showMessageDialog(parentComponent, message); } - /** * Adds title and messageType * @@ -482,6 +528,26 @@ public class JvOptionPane extends JOptionPane } /** + * 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 @@ -510,9 +576,29 @@ public class JvOptionPane extends JOptionPane public static String showInputDialog(Component parentComponent, String message, String initialSelectionValue) { - + + // AnnotationPanel + + return isInteractiveMode() + ? 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) @@ -535,13 +621,14 @@ public class JvOptionPane extends JOptionPane // 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 @@ -558,9 +645,9 @@ public class JvOptionPane extends JOptionPane Object[] selectionValues, Object initialSelectionValue) throws HeadlessException { - + // test only - + return isInteractiveMode() ? JOptionPane.showInputDialog(parentComponent, message, title, messageType, icon, selectionValues, @@ -568,8 +655,6 @@ public class JvOptionPane extends JOptionPane : getMockResponse().toString(); } - - /** * internal version * @@ -581,13 +666,12 @@ public class JvOptionPane extends JOptionPane String message) { // test only - + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, message) : getMockResponse().toString(); } - /** * internal with title and messageType * @@ -600,9 +684,9 @@ public class JvOptionPane extends JOptionPane 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) @@ -626,7 +710,7 @@ public class JvOptionPane extends JOptionPane Object[] selectionValues, Object initialSelectionValue) { // test only - + return isInteractiveMode() ? JOptionPane.showInternalInputDialog(parentComponent, message, title, messageType, icon, selectionValues, @@ -634,13 +718,12 @@ public class JvOptionPane extends JOptionPane : getMockResponse().toString(); } - ///////////// end of options /////////////// - - + private static void outputMessage(Object message) { - System.out.println(">>> JOption Message : " + message.toString()); + jalview.bin.Console + .outPrintln(">>> JOption Message : " + message.toString()); } public static Object getMockResponse() @@ -664,39 +747,33 @@ public class JvOptionPane extends JOptionPane return interactiveMode; } - public static void setInteractiveMode(boolean interactiveMode) + public static void setInteractiveMode(boolean interactive) { - JvOptionPane.interactiveMode = interactiveMode; + JvOptionPane.interactiveMode = interactive; } - @SuppressWarnings("unused") private static String getPrefix(int messageType) { - String prefix = ""; // JavaScript only + String prefix = ""; - if (/** @j2sNative true || */ - false) + // JavaScript only + if (Platform.isJS()) { switch (messageType) { - default: - case JvOptionPane.INFORMATION_MESSAGE: - prefix = "Note: "; - break; case JvOptionPane.WARNING_MESSAGE: prefix = "WARNING! "; break; case JvOptionPane.ERROR_MESSAGE: - prefix = "ERRROR! "; + prefix = "ERROR! "; break; + default: + prefix = "Note: "; } } return prefix; } - DialogRunner runner = new DialogRunner(this); - - private List ourOptions; /** * create a new option dialog that can be used to register responses - along * lines of showOptionDialog @@ -711,19 +788,39 @@ public class JvOptionPane extends JOptionPane * @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(Object message, String title, int optionType, + int messageType, Icon icon, Object[] options, Object initialValue, + boolean modal) + { + showDialog(message, title, optionType, messageType, icon, options, + initialValue, modal, null); + } + + public void showDialog(Object message, String title, int optionType, + int messageType, Icon icon, Object[] options, Object initialValue, + boolean modal, JButton[] buttons) + { if (!isInteractiveMode()) { - runner.firstRun((int) getMockResponse()); + handleResponse(getMockResponse()); + return; } // two uses: // @@ -739,94 +836,872 @@ public class JvOptionPane extends JOptionPane // // 2) UserDefinedColors warning about saving over a name already defined // - Component parent; - /** - * @j2sNative - * parent = this; - */ - { - parent = parentComponent; - } - ; + ourOptions = Arrays.asList(options); - int response = JOptionPane.showOptionDialog(parent, message, title, - optionType, messageType, icon, options, initialValue); - /** - * @j2sNative - */ + + if (modal) { - runner.firstRun(response); + boolean useButtons = false; + Object initialValueButton = null; + NOTNULL: if (buttons != null) + { + if (buttons.length != options.length) + { + jalview.bin.Console.error( + "Supplied buttons array not the same length as supplied options array."); + break NOTNULL; + } + int[] buttonActions = { JOptionPane.YES_OPTION, + JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION }; + for (int i = 0; i < options.length; i++) + { + Object o = options[i]; + jalview.bin.Console.debug( + "Setting button " + i + " to '" + o.toString() + "'"); + JButton jb = buttons[i]; + + if (o.equals(initialValue)) + initialValueButton = jb; + + int buttonAction = buttonActions[i]; + Runnable action = callbacks.get(buttonAction); + jb.setText((String) o); + jb.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + + Object obj = e.getSource(); + if (obj == null || !(obj instanceof Component)) + { + jalview.bin.Console.debug( + "Could not find Component source of event object " + + obj); + return; + } + Object joptionpaneObject = SwingUtilities.getAncestorOfClass( + JOptionPane.class, (Component) obj); + if (joptionpaneObject == null + || !(joptionpaneObject instanceof JOptionPane)) + { + jalview.bin.Console.debug( + "Could not find JOptionPane ancestor of event object " + + obj); + return; + } + JOptionPane joptionpane = (JOptionPane) joptionpaneObject; + joptionpane.setValue(buttonAction); + if (action != null) + new Thread(action).start(); + joptionpane.transferFocusBackward(); + joptionpane.setVisible(false); + // put focus and raise parent window if possible, unless cancel or + // no 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); + } + }); + + } + useButtons = true; + } + // use a JOptionPane as usual + int response = JOptionPane.showOptionDialog(parentComponent, message, + title, optionType, messageType, icon, + useButtons ? buttons : options, + useButtons ? initialValueButton : 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) + new Thread(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.setIconImages(ChannelProperties.getIconList()); + dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL + : ModalityType.MODELESS); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + setDialog(dialog); + } } - public void showInternalDialog(JPanel mainPanel, String title, + public void showInternalDialog(Object mainPanel, String title, int yesNoCancelOption, int questionMessage, Icon icon, Object[] options, String initresponse) { if (!isInteractiveMode()) { - runner.firstRun((int) getMockResponse()); - } - Component parent; - /** - * @j2sNative parent = this; - */ - { - parent = parentComponent; + handleResponse(getMockResponse()); } - ; - ourOptions = Arrays.asList(options); - - int response; - if (parent!=this) { - response = JOptionPane.showInternalOptionDialog(parent, mainPanel, - title, yesNoCancelOption, questionMessage, icon, options, - initresponse); + // need to set these separately so we can set the title bar icon later + this.setOptionType(yesNoCancelOption); + this.setMessageType(questionMessage); + this.setIcon(icon); + this.setInitialValue(initresponse); + this.setOptions(options); + this.setMessage(mainPanel); + + ourOptions = Arrays.asList(options); + if (parentComponent != this + && !(parentComponent == null && Desktop.instance == null)) + { + // note the parent goes back to a JRootPane so is probably + // Desktop.getDesktop() + JInternalFrame jif = this.createInternalFrame( + parentComponent != null ? parentComponent : Desktop.instance, + title); + // connect to the alignFrame using a map in Desktop + if (parentComponent instanceof AlignFrame) + { + Desktop.addModal((AlignFrame) parentComponent, jif); + } + jif.setFrameIcon(null); + jif.addInternalFrameListener(new InternalFrameListener() + { + @Override + public void internalFrameActivated(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameClosed(InternalFrameEvent arg0) + { + JvOptionPane.this.internalDialogHandleResponse(); + } + + @Override + public void internalFrameClosing(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameDeactivated(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameDeiconified(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameIconified(InternalFrameEvent arg0) + { + } + + @Override + public void internalFrameOpened(InternalFrameEvent arg0) + { + } + }); + jif.setVisible(true); + startModal(jif); + return; } else { - response = JOptionPane.showOptionDialog(parent, mainPanel, title, - yesNoCancelOption, questionMessage, icon, options, - initresponse); + JDialog dialog = this.createDialog(parentComponent, title); + dialog.setIconImages(ChannelProperties.getIconList()); + dialog.setVisible(true); // blocking + this.internalDialogHandleResponse(); + return; } + } + + private void internalDialogHandleResponse() + { + Object value = this.getValue(); + if (value == null + || (value instanceof Integer && (Integer) value == -1)) + { + return; + } + String responseString = value.toString(); + int response = ourOptions.indexOf(responseString); + + if (!Platform.isJS()) /** - * @j2sNative + * Java only + * + * @j2sIgnore */ { - runner.firstRun(response); + handleResponse(response); } - } + + /* + * @Override public JvOptionPane setResponseHandler(Object response, Runnable + * action) { callbacks.put(response, new Callable() { + * + * @Override public Void call() { action.run(); return null; } }); return this; + * } + */ @Override - public JvOptionPane response(RunResponse action) + public JvOptionPane setResponseHandler(Object response, Runnable action) { - - runner.response(action); + if (action == null) + { + action = NULLCALLABLE; + } + callbacks.put(response, action); return this; } - public JvOptionPane defaultResponse(Runnable runnable) + public void setDialog(JDialog d) { - runner.setDefaultResponse(runnable); - return this; + dialog = d; } + public JDialog getDialog() + { + return dialog; + } + + /** + * 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) + { + return showDialogOnTop(null, label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE); + } + + public static int showDialogOnTop(Component dialogParentComponent, + String label, String actionString, int JOPTIONPANE_OPTION, + int JOPTIONPANE_MESSAGETYPE) + { + if (!isInteractiveMode()) + { + return (int) getMockResponse(); + } + // 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(); + if (dialogParentComponent == null) + { + dialogParent.setIconImages(ChannelProperties.getIconList()); + dialogParent.setAlwaysOnTop(true); + } + + int answer = JOptionPane.showConfirmDialog( + dialogParentComponent == null ? dialogParent + : dialogParentComponent, + label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE); + + if (dialogParentComponent == null) + { + dialogParent.setAlwaysOnTop(false); + 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) + { + JFrame frame = new JFrame(); + frame.setIconImages(ChannelProperties.getIconList()); + showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal); + } + + public void showDialogOnTopAsync(JFrame dialogParent, Object label, + String actionString, int JOPTIONPANE_OPTION, + int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options, + Object initialValue, boolean modal) + { + showDialogOnTopAsync(dialogParent, label, actionString, + JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options, + initialValue, modal, null); + } + + public void showDialogOnTopAsync(JFrame dialogParent, Object label, + String actionString, int JOPTIONPANE_OPTION, + int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options, + Object initialValue, boolean modal, JButton[] buttons) + { + showDialogOnTopAsync(dialogParent, label, actionString, + JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options, + initialValue, modal, buttons, true); + } + + public void showDialogOnTopAsync(JFrame dialogParent, Object label, + String actionString, int JOPTIONPANE_OPTION, + int JOPTIONPANE_MESSAGETYPE, Icon icon, Object[] options, + Object initialValue, boolean modal, JButton[] buttons, + boolean dispose) + { + if (!isInteractiveMode()) + { + handleResponse(getMockResponse()); + return; + } + // 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) + dialogParent.setAlwaysOnTop(true); + parentComponent = dialogParent; + + showDialog(label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal, + buttons); + + if (dispose) + { + 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. + * + * @param evt + */ @Override public void propertyChange(PropertyChangeEvent evt) { - int ourOption = ourOptions.indexOf(evt.getNewValue()); - if (ourOption == -1) + Object newValue = evt.getNewValue(); + int ourOption = ourOptions.indexOf(newValue); + if (ourOption >= 0) + { + handleResponse(ourOption); + } + else { // try our luck.. - runner.run(evt.getNewValue()); + 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) + { + try + { + new Thread(action).start(); + // action.call(); + } catch (Exception e) + { + e.printStackTrace(); + } + if (parentComponent != null) + parentComponent.requestFocus(); + } + } + + /** + * Create a non-modal confirm dialog + */ + public JDialog createDialog(Component parentComponent, Object message, + String title, int optionType, int messageType, Icon icon, + Object[] options, Object initialValue, boolean modal) + { + return createDialog(parentComponent, message, title, optionType, + messageType, icon, options, initialValue, modal, null); + } + + public JDialog createDialog(Component parentComponent, Object message, + String title, int optionType, int messageType, Icon icon, + Object[] options, Object initialValue, boolean modal, + JButton[] buttons) + { + if (!isInteractiveMode()) + { + handleResponse(getMockResponse()); + return null; + } + JButton[] optionsButtons = null; + Object initialValueButton = null; + 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(); + } + if (!Platform.isJS()) // JalviewJS already uses callback, don't need to + // add them here + { + if (((optionType == JOptionPane.YES_OPTION + || optionType == JOptionPane.NO_OPTION + || optionType == JOptionPane.CANCEL_OPTION + || optionType == JOptionPane.OK_OPTION + || optionType == JOptionPane.DEFAULT_OPTION) + && options.length < 1) + || ((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"); + } + optionsButtons = new JButton[options.length]; + 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; + if (buttons != null && buttons.length > i && buttons[i] != null) + { + jb = buttons[i]; + } + else + { + jb = new JButton(); + } + jb.setText((String) o); + jb.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + joptionpane.setValue(buttonAction); + if (action != null) + new Thread(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 == 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); + } + }); + optionsButtons[i] = jb; + if (o.equals(initialValue)) + initialValueButton = jb; + } + } + joptionpane.setMessage(message); + joptionpane.setMessageType(messageType); + joptionpane.setOptionType(optionType); + joptionpane.setIcon(icon); + joptionpane.setOptions(Platform.isJS() ? options : optionsButtons); + joptionpane.setInitialValue( + Platform.isJS() ? initialValue : initialValueButton); + + JDialog dialog = joptionpane.createDialog(parentComponent, title); + dialog.setIconImages(ChannelProperties.getIconList()); + dialog.setModalityType( + modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setDialog(dialog); + 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; + } + + /** + * This helper method makes the JInternalFrame wait until it is notified by an + * InternalFrameClosing event. This method also adds the given JOptionPane to + * the JInternalFrame and sizes it according to the JInternalFrame's preferred + * size. + * + * @param f + * The JInternalFrame to make modal. + */ + private static void startModal(JInternalFrame f) + { + // We need to add an additional glasspane-like component directly + // below the frame, which intercepts all mouse events that are not + // directed at the frame itself. + JPanel modalInterceptor = new JPanel(); + modalInterceptor.setOpaque(false); + JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f); + lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue()); + modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight()); + modalInterceptor.addMouseListener(new MouseAdapter() + { + }); + modalInterceptor.addMouseMotionListener(new MouseMotionAdapter() + { + }); + lp.add(modalInterceptor); + f.toFront(); + + // disable the main menu bar if in Linux + JMenuBar menubar = null; + if (Platform.isLinux()) + { + JRootPane rootpane = Desktop.getDesktop().getRootPane(); + menubar = rootpane.getJMenuBar(); + } + + // We need to explicitly dispatch events when we are blocking the event + // dispatch thread. + EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); + try + { + if (menubar != null) + { + // don't allow clicks on main menu on linux due to a hanging bug. + // see JAL-4214. + setMenusEnabled(menubar, false); + } + + while (!f.isClosed()) + { + if (EventQueue.isDispatchThread()) + { + // The getNextEventMethod() issues wait() when no + // event is available, so we don't need do explicitly wait(). + AWTEvent ev = queue.getNextEvent(); + // This mimics EventQueue.dispatchEvent(). We can't use + // EventQueue.dispatchEvent() directly, because it is + // protected, unfortunately. + if (ev instanceof ActiveEvent) + { + ((ActiveEvent) ev).dispatch(); + } + else if (ev instanceof KeyEvent && ((KeyEvent) ev).isControlDown() + && menubar != null) + { + // temporarily enable menus to send Ctrl+? KeyEvents + setMenusEnabled(menubar, true); + ((Component) ev.getSource()).dispatchEvent(ev); + setMenusEnabled(menubar, false); + } + else if (ev.getSource() instanceof MenuComponent) + { + ((MenuComponent) ev.getSource()).dispatchEvent(ev); + } + else if (ev.getSource() instanceof Component) + { + ((Component) ev.getSource()).dispatchEvent(ev); + } + // Other events are ignored as per spec in + // EventQueue.dispatchEvent + } + else + { + // Give other threads a chance to become active. + Thread.yield(); + } + } + } catch (InterruptedException ex) + { + // If we get interrupted, then leave the modal state. + } finally + { + // re-enable the main menu bar + if (menubar != null) + { + setMenusEnabled(menubar, true); + } + + // Clean up the modal interceptor. + lp.remove(modalInterceptor); + + // unpaint the frame + f.setVisible(false); + + // close the frame + try + { + f.setClosed(true); + } catch (PropertyVetoException e) + { + f.doDefaultCloseAction(); + } + + // Remove the internal frame from its parent, so it is no longer + // lurking around and clogging memory. + Container parent = f.getParent(); + if (parent != null) + { + parent.remove(f); + } + } + } + + public static JvOptionPane frameDialog(Object message, String title, + int messageType, String[] buttonsTextS, String defaultButtonS, + List handlers, boolean modal) + { + JFrame parent = new JFrame(); + JvOptionPane jvop = JvOptionPane.newOptionDialog(); + final String[] buttonsText; + final String defaultButton; + if (buttonsTextS == null) + { + String ok = MessageManager.getString("action.ok"); + buttonsText = new String[] { ok }; + defaultButton = ok; + } + else + { + buttonsText = buttonsTextS; + defaultButton = defaultButtonS; + } + JButton[] buttons = new JButton[buttonsText.length]; + for (int i = 0; i < buttonsText.length; i++) + { + buttons[i] = new JButton(); + buttons[i].setText(buttonsText[i]); + Console.debug("DISABLING BUTTON " + buttons[i].getText()); + buttons[i].setEnabled(false); + buttons[i].setVisible(false); + } + + int dialogType = -1; + if (buttonsText.length == 1) + { + dialogType = JOptionPane.OK_OPTION; + } + else if (buttonsText.length == 2) + { + dialogType = JOptionPane.YES_NO_OPTION; } else { - runner.run(ourOption); + dialogType = JOptionPane.YES_NO_CANCEL_OPTION; + } + jvop.setResponseHandler(JOptionPane.YES_OPTION, + (handlers != null && handlers.size() > 0) ? handlers.get(0) + : NULLCALLABLE); + if (dialogType == JOptionPane.YES_NO_OPTION + || dialogType == JOptionPane.YES_NO_CANCEL_OPTION) + { + jvop.setResponseHandler(JOptionPane.NO_OPTION, + (handlers != null && handlers.size() > 1) ? handlers.get(1) + : NULLCALLABLE); } + if (dialogType == JOptionPane.YES_NO_CANCEL_OPTION) + { + jvop.setResponseHandler(JOptionPane.CANCEL_OPTION, + (handlers != null && handlers.size() > 2) ? handlers.get(2) + : NULLCALLABLE); + } + + final int dt = dialogType; + new Thread(() -> { + jvop.showDialog(message, title, dt, messageType, null, buttonsText, + defaultButton, modal, buttons); + }).start(); + + return jvop; } + private static void setMenusEnabled(JMenuBar menubar, boolean b) + { + for (int i = 0; i < menubar.getMenuCount(); i++) + { + JMenu menu = menubar.getMenu(i); + menu.setEnabled(b); + } + } }