X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fgui%2FJvOptionPane.java;h=7a5daf705eb1ba3f8856d8874fa46ca6a5255669;hb=051cab343aed415a342a787e10ebd075f05243f6;hp=5da37fe5c326aee2e9b3f244a64044cc1f3dfb07;hpb=07711595213cb89a9c42e2659c7b208b4a8b8d39;p=jalview.git diff --git a/src/jalview/gui/JvOptionPane.java b/src/jalview/gui/JvOptionPane.java index 5da37fe..7a5daf7 100644 --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@ -32,6 +32,7 @@ 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; @@ -51,8 +52,11 @@ 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; @@ -84,6 +88,13 @@ public class JvOptionPane extends JOptionPane private Map callbacks = new HashMap<>(); + private int timeout = -1; + + public void setTimeout(int i) + { + timeout = i; + } + /* * JalviewJS reports user choice in the dialog as the selected option (text); * this list allows conversion to index (int) @@ -718,7 +729,8 @@ 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() @@ -846,6 +858,42 @@ public class JvOptionPane extends JOptionPane "Supplied buttons array not the same length as supplied options array."); break NOTNULL; } + + // run through buttons for initialValue first so we can set (and start) + // a final timeoutThreadF to include (and interrupt) in the button + // actions + Thread timeoutThread = null; + for (int i = 0; i < options.length; i++) + { + Object o = options[i]; + JButton jb = buttons[i]; + if (o.equals(initialValue)) + { + if (timeout > 0 && jb != null && jb instanceof JButton) + { + // after timeout ms click the default button + timeoutThread = new Thread(() -> { + try + { + Thread.sleep(timeout); + } catch (InterruptedException e) + { + Console.debug( + "Dialog timeout interrupted. Probably a button pressed."); + } + jb.doClick(); + }); + } + initialValueButton = jb; + break; + } + } + final Thread timeoutThreadF = timeoutThread; + if (timeoutThreadF != null) + { + timeoutThreadF.start(); + } + int[] buttonActions = { JOptionPane.YES_OPTION, JOptionPane.NO_OPTION, JOptionPane.CANCEL_OPTION }; for (int i = 0; i < options.length; i++) @@ -855,9 +903,6 @@ public class JvOptionPane extends JOptionPane "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); @@ -866,6 +911,10 @@ public class JvOptionPane extends JOptionPane @Override public void actionPerformed(ActionEvent e) { + if (timeoutThreadF != null) + { + timeoutThreadF.interrupt(); + } Object obj = e.getSource(); if (obj == null || !(obj instanceof Component)) @@ -1072,53 +1121,53 @@ public class JvOptionPane extends JOptionPane 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) { - System.err.println("##### internalFrameActivated"); } @Override public void internalFrameClosed(InternalFrameEvent arg0) { - System.err.println("##### internalFrameClosed"); JvOptionPane.this.internalDialogHandleResponse(); } @Override public void internalFrameClosing(InternalFrameEvent arg0) { - System.err.println("##### internalFrameClosing"); } @Override public void internalFrameDeactivated(InternalFrameEvent arg0) { - System.err.println("##### internalFrameDeactivated"); } @Override public void internalFrameDeiconified(InternalFrameEvent arg0) { - System.err.println("##### internalFrameDeiconified"); } @Override public void internalFrameIconified(InternalFrameEvent arg0) { - System.err.println("##### internalFrameIconified"); } @Override public void internalFrameOpened(InternalFrameEvent arg0) { - System.err.println("##### internalFrameOpened"); } }); jif.setVisible(true); @@ -1137,7 +1186,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()) @@ -1254,6 +1309,17 @@ public class JvOptionPane extends JOptionPane 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()); @@ -1268,6 +1334,7 @@ public class JvOptionPane extends JOptionPane // A better hack which works is to create a new JFrame parent with // setAlwaysOnTop(true) + boolean parentOnTop = dialogParent.isAlwaysOnTop(); dialogParent.setAlwaysOnTop(true); parentComponent = dialogParent; @@ -1275,8 +1342,13 @@ public class JvOptionPane extends JOptionPane JOPTIONPANE_MESSAGETYPE, icon, options, initialValue, modal, buttons); - dialogParent.setAlwaysOnTop(false); - dialogParent.dispose(); + dialogParent.setAlwaysOnTop(parentOnTop); + + if (dispose) + { + dialogParent.setAlwaysOnTop(false); + dialogParent.dispose(); + } } /** @@ -1507,13 +1579,27 @@ 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 { - boolean stillModal = true; - while (!f.isClosed() && stillModal) + 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()) { @@ -1523,53 +1609,53 @@ public class JvOptionPane extends JOptionPane // This mimics EventQueue.dispatchEvent(). We can't use // EventQueue.dispatchEvent() directly, because it is // protected, unfortunately. - System.out.println( - "##### ev source=" + ev.getSource().getClass() + ""); if (ev instanceof ActiveEvent) { - System.err.println("##### 1"); ((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) { - System.err.println("##### 2"); ((MenuComponent) ev.getSource()).dispatchEvent(ev); } else if (ev.getSource() instanceof Component) { - System.err.println("##### 3"); - if (ev.getSource().equals(Desktop.getDesktop().getRootPane())) - { - stillModal = false; - } - else - { - ((Component) ev.getSource()).dispatchEvent(ev); - } + ((Component) ev.getSource()).dispatchEvent(ev); } // Other events are ignored as per spec in // EventQueue.dispatchEvent - System.err.println("##### 4"); } else { // Give other threads a chance to become active. - System.err.println("##### 5"); Thread.yield(); } } } catch (InterruptedException ex) { // If we get interrupted, then leave the modal state. - System.err.println("##### 6"); } finally { - System.err.println("##### 7"); + // 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); @@ -1655,4 +1741,14 @@ public class JvOptionPane extends JOptionPane 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); + } + } + }