From: Ben Soares Date: Fri, 4 Nov 2022 17:53:36 +0000 (+0000) Subject: JAL-1998 JAL-3772 JAL-3416 Merge conflict resolution of JalviewFileChooser and JvOpti... X-Git-Tag: Release_2_11_3_0~23^2~4^2~18 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=952d6b6fecfb976c0c113f013ed2b8980af3d190;p=jalview.git JAL-1998 JAL-3772 JAL-3416 Merge conflict resolution of JalviewFileChooser and JvOptionPane --- 952d6b6fecfb976c0c113f013ed2b8980af3d190 diff --cc src/jalview/gui/JvOptionPane.java index b58ea41,0b684d0..752f25c --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@@ -40,12 -47,13 +48,15 @@@ 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.JOptionPane; import javax.swing.JPanel; +import javax.swing.SwingUtilities; import javax.swing.UIManager; + import javax.swing.event.InternalFrameEvent; + import javax.swing.event.InternalFrameListener; import jalview.util.Platform; import jalview.util.dialogrunner.DialogRunnerI; @@@ -927,13 -834,102 +938,101 @@@ public class JvOptionPane extends JOpti * 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 }; - JDialog dialog = createDialog(parentComponent, message, title, - optionType, messageType, icon, options, initialValue, modal, - buttons); - jalview.bin.Console.debug("About to setVisible(true)"); + // 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 ++ 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.setIconImage(WindowIcons.logoIcon.getImage()); + dialog.setModalityType(modal ? ModalityType.APPLICATION_MODAL + : ModalityType.MODELESS); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setVisible(true); - jalview.bin.Console.debug("Just setVisible(true)"); } } @@@ -946,13 -942,60 +1045,61 @@@ handleResponse(getMockResponse()); } + // 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); + int response; if (parentComponent != this) { - response = JOptionPane.showInternalOptionDialog(parentComponent, - mainPanel, title, yesNoCancelOption, questionMessage, icon, - options, initresponse); + JInternalFrame jif = this.createInternalFrame(parentComponent, title); + jif.setFrameIcon(WindowIcons.logoIcon); + 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 { @@@ -1100,162 -1063,85 +1256,236 @@@ { return; } - Runnable action = callbacks.get(response); + Callable action = callbacks.get(response); if (action != null) { - action.run(); - parentComponent.requestFocus(); + try + { + 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) + { + 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]; + Callable 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) + 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); + } + }); + 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.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; } + + /** + * 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(); + + // We need to explicitly dispatch events when we are blocking the event + // dispatch thread. + EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); + try + { + 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.getSource() instanceof Component) + ((Component) ev.getSource()).dispatchEvent(ev); + else if (ev.getSource() instanceof MenuComponent) + ((MenuComponent) 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 + { + // Clean up the modal interceptor. + lp.remove(modalInterceptor); + + // 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); + } + } } diff --cc src/jalview/io/JalviewFileChooser.java index dba7ad1,8cd5406..077bffb --- a/src/jalview/io/JalviewFileChooser.java +++ b/src/jalview/io/JalviewFileChooser.java @@@ -21,6 -21,6 +21,12 @@@ ////////////////////////////////////////////////////////////////// package jalview.io; ++import jalview.bin.Cache; ++import jalview.gui.JvOptionPane; ++import jalview.util.MessageManager; ++import jalview.util.Platform; ++import jalview.util.dialogrunner.DialogRunnerI; ++ import java.awt.Component; import java.awt.Dimension; import java.awt.EventQueue; @@@ -43,9 -42,9 +49,10 @@@ import java.util.concurrent.Callable import javax.swing.BoxLayout; import javax.swing.DefaultListCellRenderer; import javax.swing.JCheckBox; + import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JList; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SpringLayout;