Search code examples
javaswingjoptionpanejinternalframe

Modal JInternalFrame that returns data to caller


I have a Java Swing application and a JInternalFrame..

In the JInternalFrame I have some input fields, and when I press a hotkey I want the following to happen:

  1. All execution in the current JInternalFrame is suspended and none of the input fields may recive focus.
  2. A new frame (inputFrame) will open in some kind of modal mode and present assistance to the user filling in the right value. (Data is fetched from an EJB, and filtered according to the users choice)
  3. When user click OK, inputFrame is closed and data is returned to mainFrame.
  4. mainFrame accepts the data and continues it’s processing.

If possible, I would like inputFrame to be a JInternalFrame, but it’s not a main issue.

Does anyone know a way to achieve this?


Solution

  • I found the answer in the source code for JOptionPane there I copied two static methods createInternalFrame and startModal (with just some small changes) and created my own class below:

    public class ModalInternalPanel {
    
        public static  Object showInternalDialog(Component parentComponent, Container container, HasReturnValue hasReturnValue, String title)  {
            JInternalFrame frame = createInternalFrame(parentComponent, title, container);
            startModal(frame);
            if (hasReturnValue!=null) return hasReturnValue.getReturnValue(); else return null;
        }
    
    
        public static JInternalFrame createInternalFrame(Component parentComponent, String title, Container container) throws RuntimeException  {
            // Try to find a JDesktopPane.
            JLayeredPane toUse = JOptionPane.getDesktopPaneForComponent(parentComponent);
            // If we don't have a JDesktopPane, we try to find a JLayeredPane.
            if (toUse == null)  toUse = JLayeredPane.getLayeredPaneAbove(parentComponent);
            // If this still fails, we throw a RuntimeException.
            if (toUse == null) throw new RuntimeException   ("parentComponent does not have a valid parent");
    
            JInternalFrame frame = new JInternalFrame(title); 
    
    
            frame.setContentPane(container);
            frame.setClosable(true);
    
            toUse.add(frame);
            frame.setLayer(JLayeredPane.MODAL_LAYER);
    
            frame.pack();
            frame.setVisible(true);
    
            return frame;
        }
    
    
    
        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);
            }
        }
    }