X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FJvOptionPane.java;h=5b926c3ebecff868c5e16d82154d5cb73d5edc68;hb=81d1b5ee333dee61382971eca41792d6a64a29fc;hp=5702aa0aaf7596179464ac706204e48d34530038;hpb=678fc0aaa829b3c433845ecb9bbebe7c8fd0fee5;p=jalview.git diff --git a/src/jalview/gui/JvOptionPane.java b/src/jalview/gui/JvOptionPane.java index 5702aa0..5b926c3 100644 --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@ -32,16 +32,18 @@ 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.Callable; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.swing.Icon; @@ -50,13 +52,19 @@ 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; @@ -69,9 +77,16 @@ public class JvOptionPane extends JOptionPane private static boolean interactiveMode = true; + public static final Runnable NULLCALLABLE = () -> { + }; + private Component parentComponent; - private Map> callbacks = new HashMap<>(); + 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); @@ -82,6 +97,7 @@ public class JvOptionPane extends JOptionPane public JvOptionPane(final Component parent) { this.parentComponent = Platform.isJS() ? this : parent; + this.setIcon(null); } public static int showConfirmDialog(Component parentComponent, @@ -706,7 +722,7 @@ public class JvOptionPane extends JOptionPane 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() @@ -847,7 +863,7 @@ public class JvOptionPane extends JOptionPane initialValueButton = jb; int buttonAction = buttonActions[i]; - Callable action = callbacks.get(buttonAction); + Runnable action = callbacks.get(buttonAction); jb.setText((String) o); jb.addActionListener(new ActionListener() { @@ -876,7 +892,7 @@ public class JvOptionPane extends JOptionPane JOptionPane joptionpane = (JOptionPane) joptionpaneObject; joptionpane.setValue(buttonAction); if (action != null) - Executors.newSingleThreadExecutor().submit(action); + new Thread(action).start(); joptionpane.transferFocusBackward(); joptionpane.setVisible(false); // put focus and raise parent window if possible, unless cancel or @@ -972,17 +988,18 @@ public class JvOptionPane extends JOptionPane { Object o = options[i]; int buttonAction = buttonActions[i]; - Callable action = callbacks.get(buttonAction); + 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.newSingleThreadExecutor().submit(action); + new Thread(action).start(); // joptionpane.transferFocusBackward(); joptionpane.transferFocusBackward(); joptionpane.setVisible(false); @@ -1029,15 +1046,16 @@ public class JvOptionPane extends JOptionPane Platform.isJS() ? initialValue : initialValue_btn); JDialog dialog = joptionpane.createDialog(parentComponent, title); - dialog.setIconImage(WindowIcons.logoIcon.getImage()); + 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) { @@ -1055,11 +1073,20 @@ public class JvOptionPane extends JOptionPane this.setMessage(mainPanel); ourOptions = Arrays.asList(options); - int response; - if (parentComponent != this) + if (parentComponent != this + && !(parentComponent == null && Desktop.instance == null)) { - JInternalFrame jif = this.createInternalFrame(parentComponent, title); - jif.setFrameIcon(WindowIcons.logoIcon); + // 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 @@ -1105,7 +1132,7 @@ public class JvOptionPane extends JOptionPane else { JDialog dialog = this.createDialog(parentComponent, title); - dialog.setIconImage(WindowIcons.logoIcon.getImage()); + dialog.setIconImages(ChannelProperties.getIconList()); dialog.setVisible(true); // blocking this.internalDialogHandleResponse(); return; @@ -1114,7 +1141,13 @@ public class JvOptionPane extends JOptionPane private void internalDialogHandleResponse() { - String responseString = (String) this.getValue(); + 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()) @@ -1136,13 +1169,26 @@ public class JvOptionPane extends JOptionPane * } */ @Override - public JvOptionPane setResponseHandler(Object response, - Callable action) + public JvOptionPane setResponseHandler(Object response, Runnable action) { + if (action == null) + { + action = NULLCALLABLE; + } callbacks.put(response, action); return this; } + public void setDialog(JDialog d) + { + dialog = d; + } + + public JDialog getDialog() + { + return dialog; + } + /** * showDialogOnTop will create a dialog that (attempts to) come to top of OS * desktop windows @@ -1150,6 +1196,14 @@ public class JvOptionPane extends JOptionPane 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(); @@ -1164,14 +1218,23 @@ public class JvOptionPane extends JOptionPane // A better hack which works is to create a new JFrame parent with // setAlwaysOnTop(true) JFrame dialogParent = new JFrame(); - dialogParent.setIconImage(WindowIcons.logoIcon.getImage()); - dialogParent.setAlwaysOnTop(true); + if (dialogParentComponent == null) + { + dialogParent.setIconImages(ChannelProperties.getIconList()); + dialogParent.setAlwaysOnTop(true); + } - int answer = JOptionPane.showConfirmDialog(dialogParent, label, - actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE); + int answer = JOptionPane.showConfirmDialog( + dialogParentComponent == null ? dialogParent + : dialogParentComponent, + label, actionString, JOPTIONPANE_OPTION, + JOPTIONPANE_MESSAGETYPE); - dialogParent.setAlwaysOnTop(false); - dialogParent.dispose(); + if (dialogParentComponent == null) + { + dialogParent.setAlwaysOnTop(false); + dialogParent.dispose(); + } return answer; } @@ -1181,7 +1244,7 @@ public class JvOptionPane extends JOptionPane Object[] options, Object initialValue, boolean modal) { JFrame frame = new JFrame(); - frame.setIconImage(WindowIcons.logoIcon.getImage()); + frame.setIconImages(ChannelProperties.getIconList()); showDialogOnTopAsync(frame, label, actionString, JOPTIONPANE_OPTION, JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal); } @@ -1259,12 +1322,13 @@ public class JvOptionPane extends JOptionPane { return; } - Callable action = callbacks.get(response); + Runnable action = callbacks.get(response); if (action != null) { try { - action.call(); + new Thread(action).start(); + // action.call(); } catch (Exception e) { e.printStackTrace(); @@ -1342,7 +1406,7 @@ public class JvOptionPane extends JOptionPane { Object o = options[i]; int buttonAction = buttonActions[i]; - Callable action = callbacks.get(buttonAction); + Runnable action = callbacks.get(buttonAction); JButton jb; if (buttons != null && buttons.length > i && buttons[i] != null) { @@ -1360,7 +1424,7 @@ public class JvOptionPane extends JOptionPane { joptionpane.setValue(buttonAction); if (action != null) - Executors.newSingleThreadExecutor().submit(action); + new Thread(action).start(); // joptionpane.transferFocusBackward(); joptionpane.transferFocusBackward(); joptionpane.setVisible(false); @@ -1406,10 +1470,11 @@ public class JvOptionPane extends JOptionPane Platform.isJS() ? initialValue : initialValueButton); JDialog dialog = joptionpane.createDialog(parentComponent, title); - dialog.setIconImage(WindowIcons.logoIcon.getImage()); + dialog.setIconImages(ChannelProperties.getIconList()); dialog.setModalityType( modal ? ModalityType.APPLICATION_MODAL : ModalityType.MODELESS); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + setDialog(dialog); return dialog; } @@ -1452,11 +1517,26 @@ public class JvOptionPane extends JOptionPane 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()) @@ -1468,11 +1548,25 @@ public class JvOptionPane extends JOptionPane // EventQueue.dispatchEvent() directly, because it is // protected, unfortunately. if (ev instanceof ActiveEvent) + { ((ActiveEvent) ev).dispatch(); - else if (ev.getSource() instanceof Component) + } + 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 } @@ -1487,14 +1581,112 @@ public class JvOptionPane extends JOptionPane // 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 + { + 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); + } + } + }