JOptionPane.showInternalConfirmDialog() frame icon

403 Views Asked by At

I have an application using a JDesktopPane and I am using JOptionPane.showInternalConfirmDialog() to get confirmation from the user. The issue I am having is that no matter what I use as the parent, I always get the coffee cup as the frame icon. See example code (without try-catch):

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(300, 300);     
frame.setIconImage(ImageIO.read(new File("icon.png")));

JDesktopPane dp = new JDesktopPane();

frame.add(dp);
frame.setVisible(true);
        
if(JOptionPane.showInternalConfirmDialog(frame.getContentPane(),
   "Are you sure?", "Confirm", JOptionPane.YES_NO_OPTION) == 0)
{
   //Do something
}

This is what I get:

Screenshot

If I use the frame as the parent instead of using getContentPane() I get this exception:

Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: JOptionPane: parentComponent does not have a valid parent

Would appreciate any insight into this issue.

EDIT: I am aware of the workaround of creating a new internal frame as follows:

JOptionPane jop = new JOptionPane("Are you sure?", JOptionPane.YES_NO_OPTION);
JInternalFrame inf = jop.createInternalFrame(frame, "Confirm");
inf.setFrameIcon(icon);
inf.setVisible(true);

But then the window is no longer modal, which I need it to be.

3

There are 3 best solutions below

4
instanceof On

As per the docs, you can pass an Icon as a parameter to the showInternalConfirmDialog method. Just remember to also then add all the rest of the parameters in the correct order.

1
gthanop On

Well, in order to change the dialog's icon image, you need to obtain a reference to the dialog that is created by the JOptionPanes' static methods.

The static methods of JOptionPane work about as follows:

  1. Create a JOptionPane and prepare it with the arguments.
  2. Create a dialog (or internal frame) and prepare it with the arguments.
  3. Add the JOptionPane (created from step 1) to the dialog (or internal frame) (created from step 2) with the required listeners.
  4. Show the dialog as modal (blocks for input).
  5. Search the user's supplied value from the selected ones and return it.

Step 2 and 3 take place in the create* methods of JOptionPane.

So you may try the following:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.Objects;
import javax.swing.JDesktopPane;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

public class OptionPaneCustomIconDialog {
    
    public static void main(final String[] args) {
        SwingUtilities.invokeLater(() -> {
            
            final JDesktopPane pane = new JDesktopPane();
            pane.setPreferredSize(new Dimension(500, 500));
            
            final JFrame frame = new JFrame("No coffee...");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.getContentPane().add(pane);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            
            final BufferedImage dialogImage = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB);
            final Graphics2D g2d = dialogImage.createGraphics();
            g2d.setColor(Color.RED);
            g2d.fillRect(25, 25, 50, 50);
            g2d.dispose();
            
            final JOptionPane option = new JOptionPane("Are you sure?", JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION);
            final JDialog diag = option.createDialog(pane, "Please confirm...");
            diag.setIconImage(dialogImage); //The solution.
            diag.setVisible(true); //Blocks (because it's modal).
            final Object value = option.getValue();
            if (value == null || Objects.equals(value, JOptionPane.CLOSED_OPTION))
                System.out.println("Closed");
            else if (Objects.equals(value, JOptionPane.YES_OPTION))
                System.out.println("Yes");
            else if (Objects.equals(value, JOptionPane.NO_OPTION))
                System.out.println("No");
            else
                System.err.println("Please implement all options...");
        });
    }
}
0
jbslade On

Okay, this is the solution I came up with. I had a look at JOptionPane and it uses a private method called startModal() to make the internal frame modal.

I copied that method and the static showInternalConfirmDialog() to a new class, added an ImageIcon as an argument and used setFrameIcon() to change the icon.

Note: I also had to use setVisible() and requestFocus() on the internal frame to make it work.

import java.awt.AWTEvent;
import java.awt.ActiveEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.MenuComponent;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
import javax.swing.ImageIcon;
import javax.swing.JInternalFrame;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class InternalDialog {
    
    public static int showInternalConfirmDialog(Component parentComponent,
                                               Object message, String title,
                                               int optionType, int messageType,
                                               ImageIcon icon)
   {
     JOptionPane pane = new JOptionPane(message, messageType, optionType);
     JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
     
     //WHAT I ADDED
     frame.setFrameIcon(icon);
     frame.setVisible(true);
     frame.requestFocus();
 
     startModal(frame);
 
     if (pane.getValue() instanceof Integer)
       return ((Integer) pane.getValue()).intValue();
     return -1;
   }
    
    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);
        }
    }
}

Usage:

JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(300, 300);

ImageIcon icon = ImageIO.read(new File("icon.png"));     
frame.setIconImage(icon);

JDesktopPane dp = new JDesktopPane();
frame.add(dp);
frame.setVisible(true);
        
if(InternalDialog.showInternalConfirmDialog(frame.getContentPane(),
  "Are you sure?", "Confirm", JOptionPane.YES_NO_OPTION, 
  JOptionPane.QUESTION_MESSAGE, icon) == 0)
{
   //Do something
}

The startModal() method might be useful in general for anyone trying to make a modal internal frame.